From 5ffd4b441332edaf938fb1e62bc0f7d7d4070fba Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Mon, 11 Mar 2024 17:52:36 +0300 Subject: [PATCH 01/22] introduce an auth map to store optimized authentication path --- crypto/src/merkle_tree/merkle.rs | 117 +++++++++++++++++++++++++++++-- crypto/src/merkle_tree/mod.rs | 1 + crypto/src/merkle_tree/utils.rs | 16 ++--- 3 files changed, 121 insertions(+), 13 deletions(-) diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index 42c4494ac..bbf65b23f 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -1,9 +1,12 @@ use core::fmt::Display; +use std::collections::HashMap; use alloc::vec::Vec; use super::{proof::Proof, traits::IsMerkleTreeBackend, utils::*}; +pub type NodePos = usize; + #[derive(Debug)] pub enum Error { OutOfBounds, @@ -53,7 +56,8 @@ where } pub fn get_proof_by_pos(&self, pos: usize) -> Option> { - let pos = pos + self.nodes.len() / 2; + let first_leaf_index = self.nodes.len() / 2; + let pos = pos + first_leaf_index; let Ok(merkle_path) = self.build_merkle_path(pos) else { return None; }; @@ -61,30 +65,95 @@ where self.create_proof(merkle_path) } + // If number of leaves is even, height of this tree will be one less than the whole merkle tree, + // because we don't want to store leaves again here. + // If number of leaves is odd, height of this tree will be equal to the whole merkle tree, but + // the highest level will contain just 1 leaf, completing its sibling. + // ending_leaf_index starts from 0 for the first leaf, disregarding inner nodes of the tree. + pub fn populate_auth_map<'a>( + &'a self, + auth_map: &mut HashMap, + ending_leaf_index: usize, + ) -> Result<(), Error> { + assert!(auth_map.is_empty()); + + let first_leaf_pos = self.nodes.len() / 2; + let mut current_leaf_index = ending_leaf_index; + // Get the position in all tree + let mut pos = current_leaf_index + first_leaf_pos; + // If number of leaves included in the batch proof is odd, then we include the right sibling + // of the last leaf in the auth_map. + if current_leaf_index + 1 % 2 == 1 { + self.add_to_auth_map_if_not_contains(auth_map, get_sibling_pos(pos))?; + } + + // O(n), where n is number of consecutive leaves included in batch proof + while current_leaf_index > 0 { + // Don't include the leaves in the auth tree + pos = get_parent_pos(pos); + + // O(logN), where N is the number of all leaves in the merkle tree + // However, theta will be lower than this. + while pos != ROOT { + // Go to the next leaf if current path is issued before + if !self.add_to_auth_map_if_not_contains(auth_map, get_sibling_pos(pos))? { + break; + } + pos = get_parent_pos(pos); + } + + current_leaf_index -= 1; + pos = current_leaf_index + first_leaf_pos; + } + + Ok(()) + } + fn create_proof(&self, merkle_path: Vec) -> Option> { Some(Proof { merkle_path }) } + // pos parameter is the index in overall Merkle tree, including the inner nodes fn build_merkle_path(&self, pos: usize) -> Result, Error> { let mut merkle_path = Vec::new(); let mut pos = pos; while pos != ROOT { - let Some(node) = self.nodes.get(sibling_index(pos)) else { + let Some(node) = self.nodes.get(get_sibling_pos(pos)) else { // out of bounds, exit returning the current merkle_path return Err(Error::OutOfBounds); }; merkle_path.push(node.clone()); - pos = parent_index(pos); + pos = get_parent_pos(pos); } Ok(merkle_path) } + + fn add_to_auth_map_if_not_contains<'a>( + &'a self, + auth_map: &mut HashMap, + index: NodePos, + ) -> Result { + let Some(node) = self.nodes.get(index) else { + return Err(Error::OutOfBounds); + }; + + if auth_map.contains_key(&index) { + return Ok(false); + } + + auth_map.insert(index, node); + + Ok(true) + } } #[cfg(test)] mod tests { + use std::collections::HashSet; + use super::*; use lambdaworks_math::field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}; @@ -93,11 +162,13 @@ mod tests { const MODULUS: u64 = 13; type U64PF = U64PrimeField; type FE = FieldElement; + type Node = > as IsMerkleTreeBackend>::Node; + type TestTree = MerkleTree>; #[test] // expected | 10 | 3 | 7 | 1 | 2 | 3 | 4 | fn build_merkle_tree_from_a_power_of_two_list_of_values() { let values: Vec = (1..5).map(FE::new).collect(); - let merkle_tree = MerkleTree::>::build(&values); + let merkle_tree = TestTree::build(&values); assert_eq!(merkle_tree.root, FE::new(20)); } @@ -109,7 +180,43 @@ mod tests { type FE = FieldElement; let values: Vec = (1..6).map(FE::new).collect(); - let merkle_tree = MerkleTree::>::build(&values); + let merkle_tree = TestTree::build(&values); assert_eq!(merkle_tree.root, FE::new(8)); } + + fn print_indices(tree_length: usize, mark_indices: HashSet) { + let depth = (tree_length as f64).log2().ceil() as usize; + let mut index = 0; + + for i in 0..depth { + let elements_at_this_depth = 2usize.pow(i as u32); + let padding = 2usize.pow((depth - i) as u32 + 1); + + for _ in 0..elements_at_this_depth { + if index >= tree_length { + continue; + } + if mark_indices.contains(&index) { + print!("{:width$}.", index, width = padding - 1); + } else { + print!("{:width$}", index, width = padding); + } + index += 1; + } + println!(); + } + } + + #[test] + fn build_auth_map() { + let values: Vec = (1..u64::pow(2, 4)).map(FE::new).collect(); + let merkle_tree = TestTree::build(&values); + + print_indices(merkle_tree.nodes.len(), HashSet::new()); + + let mut auth_map: HashMap = HashMap::new(); + TestTree::populate_auth_map(&merkle_tree, &mut auth_map, 11).unwrap(); + + print_indices(merkle_tree.nodes.len(), auth_map.keys().cloned().collect()); + } } diff --git a/crypto/src/merkle_tree/mod.rs b/crypto/src/merkle_tree/mod.rs index 5fee9b7c8..8f2f57773 100644 --- a/crypto/src/merkle_tree/mod.rs +++ b/crypto/src/merkle_tree/mod.rs @@ -1,4 +1,5 @@ pub mod backends; +pub mod batch_proof; pub mod merkle; pub mod proof; #[cfg(test)] diff --git a/crypto/src/merkle_tree/utils.rs b/crypto/src/merkle_tree/utils.rs index 87cb9b9bb..43b07f230 100644 --- a/crypto/src/merkle_tree/utils.rs +++ b/crypto/src/merkle_tree/utils.rs @@ -4,19 +4,19 @@ use super::traits::IsMerkleTreeBackend; #[cfg(feature = "parallel")] use rayon::prelude::*; -pub fn sibling_index(node_index: usize) -> usize { - if node_index % 2 == 0 { - node_index - 1 +pub fn get_sibling_pos(node_pos: usize) -> usize { + if node_pos % 2 == 0 { + node_pos - 1 } else { - node_index + 1 + node_pos + 1 } } -pub fn parent_index(node_index: usize) -> usize { - if node_index % 2 == 0 { - (node_index - 1) / 2 +pub fn get_parent_pos(node_pos: usize) -> usize { + if node_pos % 2 == 0 { + (node_pos - 1) / 2 } else { - node_index / 2 + node_pos / 2 } } From f0483bb84a041c515462e1f77935b8be3dccb360 Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Mon, 11 Mar 2024 18:24:34 +0300 Subject: [PATCH 02/22] Index-independent approach --- crypto/src/merkle_tree/merkle.rs | 99 +++++++++++++++----------------- crypto/src/merkle_tree/mod.rs | 1 - 2 files changed, 47 insertions(+), 53 deletions(-) diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index bbf65b23f..b9a569777 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -1,5 +1,5 @@ use core::fmt::Display; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use alloc::vec::Vec; @@ -55,6 +55,10 @@ where } } + pub fn levels(&self) -> usize { + (self.nodes.len() as f32).log2().ceil() as usize + } + pub fn get_proof_by_pos(&self, pos: usize) -> Option> { let first_leaf_index = self.nodes.len() / 2; let pos = pos + first_leaf_index; @@ -65,50 +69,6 @@ where self.create_proof(merkle_path) } - // If number of leaves is even, height of this tree will be one less than the whole merkle tree, - // because we don't want to store leaves again here. - // If number of leaves is odd, height of this tree will be equal to the whole merkle tree, but - // the highest level will contain just 1 leaf, completing its sibling. - // ending_leaf_index starts from 0 for the first leaf, disregarding inner nodes of the tree. - pub fn populate_auth_map<'a>( - &'a self, - auth_map: &mut HashMap, - ending_leaf_index: usize, - ) -> Result<(), Error> { - assert!(auth_map.is_empty()); - - let first_leaf_pos = self.nodes.len() / 2; - let mut current_leaf_index = ending_leaf_index; - // Get the position in all tree - let mut pos = current_leaf_index + first_leaf_pos; - // If number of leaves included in the batch proof is odd, then we include the right sibling - // of the last leaf in the auth_map. - if current_leaf_index + 1 % 2 == 1 { - self.add_to_auth_map_if_not_contains(auth_map, get_sibling_pos(pos))?; - } - - // O(n), where n is number of consecutive leaves included in batch proof - while current_leaf_index > 0 { - // Don't include the leaves in the auth tree - pos = get_parent_pos(pos); - - // O(logN), where N is the number of all leaves in the merkle tree - // However, theta will be lower than this. - while pos != ROOT { - // Go to the next leaf if current path is issued before - if !self.add_to_auth_map_if_not_contains(auth_map, get_sibling_pos(pos))? { - break; - } - pos = get_parent_pos(pos); - } - - current_leaf_index -= 1; - pos = current_leaf_index + first_leaf_pos; - } - - Ok(()) - } - fn create_proof(&self, merkle_path: Vec) -> Option> { Some(Proof { merkle_path }) } @@ -131,20 +91,46 @@ where Ok(merkle_path) } + pub fn populate_auth_map<'a>( + &'a self, + auth_map: &mut HashMap, + leaf_positions: &mut [NodePos], + ) -> Result<(), Error> { + assert!(auth_map.is_empty()); + + // let first_leaf_pos = self.nodes.len() / 2; + // let mut obtainable_nodes: HashSet<_> = leaf_positions.iter().cloned().collect(); + + for leaf_pos in leaf_positions { + let mut pos = get_parent_pos(*leaf_pos); + // O(logN), where N is the number of all leaves in the merkle tree + // However, theta will be lower than this. + while pos != ROOT { + // Go to the next leaf if current path is issued before + if !self.add_to_auth_map_if_not_contains(auth_map, get_sibling_pos(pos))? { + break; + } + pos = get_parent_pos(pos); + } + } + + Ok(()) + } + fn add_to_auth_map_if_not_contains<'a>( &'a self, auth_map: &mut HashMap, - index: NodePos, + pos: NodePos, ) -> Result { - let Some(node) = self.nodes.get(index) else { + let Some(node) = self.nodes.get(pos) else { return Err(Error::OutOfBounds); }; - if auth_map.contains_key(&index) { + if auth_map.contains_key(&pos) { return Ok(false); } - auth_map.insert(index, node); + auth_map.insert(pos, node); Ok(true) } @@ -209,13 +195,22 @@ mod tests { #[test] fn build_auth_map() { - let values: Vec = (1..u64::pow(2, 4)).map(FE::new).collect(); - let merkle_tree = TestTree::build(&values); + let leaf_values: Vec = (1..u64::pow(2, 4)).map(FE::new).collect(); + let merkle_tree = TestTree::build(&leaf_values); print_indices(merkle_tree.nodes.len(), HashSet::new()); + let nodes_len = merkle_tree.nodes.len(); + let first_leaf_pos = nodes_len / 2; + let mut leaf_positions: Vec<_> = (0..leaf_values.len()) + .map(|i| (i + first_leaf_pos)) + .collect(); + + // Build an authentication map for the first 10 leaves + let mut auth_map: HashMap = HashMap::new(); - TestTree::populate_auth_map(&merkle_tree, &mut auth_map, 11).unwrap(); + TestTree::populate_auth_map(&merkle_tree, &mut auth_map, &mut leaf_positions[..10]) + .unwrap(); print_indices(merkle_tree.nodes.len(), auth_map.keys().cloned().collect()); } diff --git a/crypto/src/merkle_tree/mod.rs b/crypto/src/merkle_tree/mod.rs index 8f2f57773..5fee9b7c8 100644 --- a/crypto/src/merkle_tree/mod.rs +++ b/crypto/src/merkle_tree/mod.rs @@ -1,5 +1,4 @@ pub mod backends; -pub mod batch_proof; pub mod merkle; pub mod proof; #[cfg(test)] From 6935de0f53f4a968e958cff980ab239f71b7f31b Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Wed, 13 Mar 2024 22:48:26 +0300 Subject: [PATCH 03/22] verify first batch proof --- .../src/merkle_tree/backends/field_element.rs | 8 +- .../backends/field_element_vector.rs | 10 +- crypto/src/merkle_tree/batch_proof.rs | 249 ++++++++++++++++++ crypto/src/merkle_tree/merkle.rs | 197 +++++++------- crypto/src/merkle_tree/mod.rs | 1 + crypto/src/merkle_tree/proof.rs | 4 +- examples/merkle-tree-cli/src/main.rs | 2 +- provers/stark/src/fri/mod.rs | 2 +- provers/stark/src/prover.rs | 8 +- 9 files changed, 365 insertions(+), 116 deletions(-) create mode 100644 crypto/src/merkle_tree/batch_proof.rs diff --git a/crypto/src/merkle_tree/backends/field_element.rs b/crypto/src/merkle_tree/backends/field_element.rs index fddc23630..bdadbbeb8 100644 --- a/crypto/src/merkle_tree/backends/field_element.rs +++ b/crypto/src/merkle_tree/backends/field_element.rs @@ -92,7 +92,7 @@ mod tests { fn hash_data_field_element_backend_works_with_keccak_256() { let values: Vec = (1..6).map(FE::from).collect(); let merkle_tree = MerkleTree::>::build(&values); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + let proof = merkle_tree.get_proof(0).unwrap(); assert!(proof.verify::>( &merkle_tree.root, 0, @@ -104,7 +104,7 @@ mod tests { fn hash_data_field_element_backend_works_with_sha3_256() { let values: Vec = (1..6).map(FE::from).collect(); let merkle_tree = MerkleTree::>::build(&values); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + let proof = merkle_tree.get_proof(0).unwrap(); assert!(proof.verify::>( &merkle_tree.root, 0, @@ -116,7 +116,7 @@ mod tests { fn hash_data_field_element_backend_works_with_keccak_512() { let values: Vec = (1..6).map(FE::from).collect(); let merkle_tree = MerkleTree::>::build(&values); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + let proof = merkle_tree.get_proof(0).unwrap(); assert!(proof.verify::>( &merkle_tree.root, 0, @@ -128,7 +128,7 @@ mod tests { fn hash_data_field_element_backend_works_with_sha3_512() { let values: Vec = (1..6).map(FE::from).collect(); let merkle_tree = MerkleTree::>::build(&values); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + let proof = merkle_tree.get_proof(0).unwrap(); assert!(proof.verify::>( &merkle_tree.root, 0, diff --git a/crypto/src/merkle_tree/backends/field_element_vector.rs b/crypto/src/merkle_tree/backends/field_element_vector.rs index 4c042e4aa..42cd0db2b 100644 --- a/crypto/src/merkle_tree/backends/field_element_vector.rs +++ b/crypto/src/merkle_tree/backends/field_element_vector.rs @@ -112,7 +112,7 @@ mod tests { vec![FE::from(9u64), FE::from(21u64)], ]; let merkle_tree = MerkleTree::>::build(&values); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + let proof = merkle_tree.get_proof(0).unwrap(); assert!(proof.verify::>( &merkle_tree.root, 0, @@ -133,7 +133,7 @@ mod tests { vec![FE::from(9u64), FE::from(21u64)], ]; let merkle_tree = MerkleTree::>::build(&values); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + let proof = merkle_tree.get_proof(0).unwrap(); assert!(proof.verify::>( &merkle_tree.root, 0, @@ -154,7 +154,7 @@ mod tests { vec![FE::from(9u64), FE::from(21u64)], ]; let merkle_tree = MerkleTree::>::build(&values); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + let proof = merkle_tree.get_proof(0).unwrap(); assert!(proof.verify::>( &merkle_tree.root, 0, @@ -175,7 +175,7 @@ mod tests { vec![FE::from(9u64), FE::from(21u64)], ]; let merkle_tree = MerkleTree::>::build(&values); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + let proof = merkle_tree.get_proof(0).unwrap(); assert!(proof.verify::>( &merkle_tree.root, 0, @@ -196,7 +196,7 @@ mod tests { vec![FE::from(9u64), FE::from(21u64)], ]; let merkle_tree = MerkleTree::>::build(&values); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + let proof = merkle_tree.get_proof(0).unwrap(); assert!(proof.verify::>( &merkle_tree.root, 0, diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs new file mode 100644 index 000000000..189987083 --- /dev/null +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -0,0 +1,249 @@ +use std::collections::HashMap; + +use alloc::vec::Vec; +#[cfg(feature = "alloc")] +use lambdaworks_math::traits::Serializable; +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +use lambdaworks_math::{errors::DeserializationError, traits::Deserializable}; + +use crate::merkle_tree::utils::get_parent_pos; + +use super::{merkle::NodePos, traits::IsMerkleTreeBackend, utils::get_sibling_pos}; + +/// Stores a merkle path to some leaf. +/// Internally, the necessary hashes are stored from root to leaf in the +/// `merkle_path` field, in such a way that, if the merkle tree is of height `n`, the +/// `i`-th element of `merkle_path` is the sibling node in the `n - 1 - i`-th check +/// when verifying. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BatchProof { + pub auth: HashMap, +} + +impl BatchProof { + pub fn verify(&self, root_hash: B::Node, hashed_leaves: HashMap) -> bool + where + B: IsMerkleTreeBackend, + { + let root_pos = 0; + + // Iterate the levels starting from the leaves, and build the upper level only using + // the provided leaves and the auth map. + let mut current_level = hashed_leaves; + loop { + let mut parent_level = HashMap::::new(); + + for (pos, node) in current_level.iter() { + // Levels are expected to have tuples of nodes. If the first one was + // already processed and parent was set, skip the sibling. + let parent_pos = get_parent_pos(*pos); + if parent_level.contains_key(&parent_pos) { + continue; + } + + // Get the sibling node from the current level. + // If doesn't exist, then it must have been provided in the batch auth. + // If neither, then verification fails. + let sibling_pos = get_sibling_pos(*pos); + let sibling_node = if current_level.contains_key(&sibling_pos) { + current_level.get(&sibling_pos).unwrap() + } else if self.auth.contains_key(&sibling_pos) { + self.auth.get(&sibling_pos).unwrap() + } else { + // no sibling to hash with! Return error. + panic!(); + }; + + let parent_node = B::hash_new_parent(node, sibling_node); + + // Root must match the provided root hash. + if parent_pos == root_pos { + return parent_node == root_hash; + } + + // Create a new element for the next, upper level + parent_level.insert(parent_pos, parent_node); + } + + // We didn't create any parents, and we didn't reach the root neither. Verification fails. + if parent_level.is_empty() { + return false; + } + + // Issue the next level in the next iteration + current_level = parent_level; + } + } +} + +// #[cfg(feature = "alloc")] +// impl Serializable for BatchProof +// where +// T: Serializable + PartialEq + Eq, +// { +// fn serialize(&self) -> Vec { +// self.merkle_path +// .iter() +// .flat_map(|node| node.serialize()) +// .collect() +// } +// } + +// impl Deserializable for BatchProof +// where +// T: Deserializable + PartialEq + Eq, +// { +// fn deserialize(bytes: &[u8]) -> Result +// where +// Self: Sized, +// { +// let mut auth = Vec::new(); +// for elem in bytes[0..].chunks(8) { +// let node = T::deserialize(elem)?; +// auth.push(node); +// } +// Ok(Self { auth }) +// } +// } + +#[cfg(test)] +mod tests { + + use std::collections::{HashMap, HashSet}; + + #[cfg(feature = "alloc")] + use super::BatchProof; + use alloc::vec::Vec; + use lambdaworks_math::field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}; + #[cfg(feature = "alloc")] + use lambdaworks_math::traits::{Deserializable, Serializable}; + + use crate::merkle_tree::{ + merkle::{MerkleTree, NodePos}, + test_merkle::TestBackend as TB, + traits::IsMerkleTreeBackend, + }; + + /// Small field useful for starks, sometimes called min i goldilocks + /// Used in miden and winterfell + // This field shouldn't be defined inside the merkle tree module + pub type Ecgfp5 = U64PrimeField<0xFFFF_FFFF_0000_0001_u64>; + pub type Ecgfp5FE = FieldElement; + pub type TestBackend = TB; + pub type TestMerkleTreeEcgfp = MerkleTree; + #[cfg(feature = "alloc")] + pub type TestBatchProofEcgfp5 = BatchProof; + + const MODULUS: u64 = 13; + type U64PF = U64PrimeField; + type FE = FieldElement; + + // #[test] + // #[cfg(feature = "alloc")] + // fn serialize_batch_proof_and_deserialize_using_be_it_get_a_consistent_batch_proof() { + // let merkle_path = [Ecgfp5FE::new(2), Ecgfp5FE::new(1), Ecgfp5FE::new(1)].to_vec(); + // let original_batch_proof = TestBatchProofEcgfp5 { merkle_path }; + // let serialize_batch_proof = original_batch_proof.serialize(); + // let batch_proof: TestBatchProofEcgfp5 = + // BatchProof::deserialize(&serialize_batch_proof).unwrap(); + + // for (o_node, node) in original_batch_proof + // .merkle_path + // .iter() + // .zip(batch_proof.merkle_path) + // { + // assert_eq!(*o_node, node); + // } + // } + + // #[test] + // #[cfg(feature = "alloc")] + // fn serialize_batch_proof_and_deserialize_using_le_it_get_a_consistent_batch_proof() { + // let merkle_path = [Ecgfp5FE::new(2), Ecgfp5FE::new(1), Ecgfp5FE::new(1)].to_vec(); + // let original_batch_proof = TestBatchProofEcgfp5 { merkle_path }; + // let serialize_batch_proof = original_batch_proof.serialize(); + // let batch_proof: TestBatchProofEcgfp5 = + // BatchProof::deserialize(&serialize_batch_proof).unwrap(); + + // for (o_node, node) in original_batch_proof + // .merkle_path + // .iter() + // .zip(batch_proof.merkle_path) + // { + // assert_eq!(*o_node, node); + // } + // } + + // Creates following tree: + // + // 20 + // / \ + // 6 14 + // / \ / \ + // 2 4 6 8 + // + // Proves inclusion of leaves whose indices are passed into 'leaf_indices' array. + // If it's [0, 3], then the test will create proof and verify inclusion of leaves with indices 0 and 3, + // that are, 2 and 8. + // + // The test uses a test backend whose hash function is just an element added to itself. + // So if leaf_values = [1,2,3,4], then actual leaf values will be [2,4,6,8], making the root 20. + #[test] + fn batch_proof_pen_and_paper_example() { + let leaf_values: Vec = (1..u64::pow(2, 2) + 1).map(Ecgfp5FE::new).collect(); + let merkle_tree: MerkleTree = TestMerkleTreeEcgfp::build(&leaf_values); + + let nodes_len = merkle_tree.nodes_len(); + let first_leaf_pos = nodes_len / 2; + + // Leaves to prove inclusion for + let leaf_indices = [0, 3]; + let leaf_positions: Vec = leaf_indices + .iter() + .map(|leaf_index| leaf_index + first_leaf_pos) + .collect(); + + // Build an authentication map for the first 10 leaves + let batch_proof = merkle_tree.get_batch_proof(&leaf_positions).unwrap(); + + let leaves: HashMap = leaf_positions + .iter() + .map(|pos| { + ( + *pos, + TestBackend::hash_data(&leaf_values[*pos - first_leaf_pos].clone()), + ) + }) + .collect(); + + assert!(batch_proof.verify::(merkle_tree.root, leaves)); + } + + // #[test] + // fn create_a_merkle_tree_with_10000_elements_and_verify_that_an_element_is_part_of_it() { + // let values: Vec = (1..10000).map(Ecgfp5FE::new).collect(); + // let merkle_tree = TestMerkleTreeEcgfp::build(&values); + + // let proven_leaf_range = 4..usize::pow(2, 8); + + // let batch_proof = merkle_tree + // .get_batch_proof(&proven_leaf_range.clone().collect::>()) + // .unwrap(); + + // let leaves = HashMap::from_iter( + // merkle_tree + // .get_batch_with_positions( + // &proven_leaf_range + // .map(|e| NodePos::from(e)) + // .collect::>(), + // ) + // .iter() + // .map(|(pos, elem)| (*pos, elem.clone())), + // ); + + // assert!(batch_proof.verify::>(merkle_tree.root, leaves)); + // } +} diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index b9a569777..bf7e505d1 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -1,11 +1,12 @@ use core::fmt::Display; -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use alloc::vec::Vec; -use super::{proof::Proof, traits::IsMerkleTreeBackend, utils::*}; +use super::{batch_proof::BatchProof, proof::Proof, traits::IsMerkleTreeBackend, utils::*}; pub type NodePos = usize; +const ROOT: NodePos = 0; #[derive(Debug)] pub enum Error { @@ -28,7 +29,29 @@ pub struct MerkleTree { nodes: Vec, } -const ROOT: usize = 0; +#[cfg(test)] +fn print_positions(tree_length: usize, mark_positions: HashSet) { + let depth = (tree_length as f64).log2().ceil() as usize; + let mut index = 0; + + for i in 0..depth { + let elements_at_this_depth = 2usize.pow(i as u32); + let padding = 2usize.pow((depth - i) as u32 + 1); + + for _ in 0..elements_at_this_depth { + if index >= tree_length { + continue; + } + if mark_positions.contains(&index) { + print!("{:width$}.", index, width = padding - 1); + } else { + print!("{:width$}", index, width = padding); + } + index += 1; + } + println!(); + } +} impl MerkleTree where @@ -49,32 +72,53 @@ where //Build the inner nodes of the tree build::(&mut nodes, leaves_len); + #[cfg(test)] + print_positions(nodes.len(), HashSet::new()); + MerkleTree { root: nodes[ROOT].clone(), nodes, } } - pub fn levels(&self) -> usize { - (self.nodes.len() as f32).log2().ceil() as usize - } - - pub fn get_proof_by_pos(&self, pos: usize) -> Option> { - let first_leaf_index = self.nodes.len() / 2; + pub fn get_proof(&self, pos: NodePos) -> Option> { + let first_leaf_index = self.nodes_len() / 2; let pos = pos + first_leaf_index; let Ok(merkle_path) = self.build_merkle_path(pos) else { return None; }; - self.create_proof(merkle_path) + Some(Proof { merkle_path }) } - fn create_proof(&self, merkle_path: Vec) -> Option> { - Some(Proof { merkle_path }) + /// Builds and returns a batch proof for when a Merkle tree is used to prove inclusion of multiple leaves. + /// + /// pos_list is a list of leaf positions (within all tree) to create a batch inclusion proof for. + /// pos_list need not be continuous, but the resulting proof becomes the smallest when so. + pub fn get_batch_proof(&self, pos_list: &[NodePos]) -> Option> { + let batch_auth_path_positions = self.get_batch_auth_path_positions(pos_list); + #[cfg(test)] + print_positions( + self.nodes_len(), + batch_auth_path_positions.iter().cloned().collect(), + ); + let batch_auth_path_nodes_iter = batch_auth_path_positions + .iter() + .map(|pos| (*pos, self.nodes[*pos].clone()).clone()); + + Some(BatchProof { + auth: batch_auth_path_nodes_iter.collect(), + }) + } + + pub fn nodes_len(&self) -> usize { + self.nodes.len() } - // pos parameter is the index in overall Merkle tree, including the inner nodes - fn build_merkle_path(&self, pos: usize) -> Result, Error> { + /// Builds and returns a proof of inclusion for the leaf whose position is passed as an argument. + /// + /// pos parameter is the index in overall Merkle tree, including the inner nodes + fn build_merkle_path(&self, pos: NodePos) -> Result, Error> { let mut merkle_path = Vec::new(); let mut pos = pos; @@ -91,55 +135,58 @@ where Ok(merkle_path) } - pub fn populate_auth_map<'a>( - &'a self, - auth_map: &mut HashMap, - leaf_positions: &mut [NodePos], - ) -> Result<(), Error> { - assert!(auth_map.is_empty()); - - // let first_leaf_pos = self.nodes.len() / 2; - // let mut obtainable_nodes: HashSet<_> = leaf_positions.iter().cloned().collect(); - - for leaf_pos in leaf_positions { - let mut pos = get_parent_pos(*leaf_pos); - // O(logN), where N is the number of all leaves in the merkle tree - // However, theta will be lower than this. - while pos != ROOT { - // Go to the next leaf if current path is issued before - if !self.add_to_auth_map_if_not_contains(auth_map, get_sibling_pos(pos))? { - break; + /// Batch Merkle proofs require multiple authentication paths to be computed, and some nodes in these paths + /// can be obtained from the leaves that are subject to the batch Merkle proof. + /// This function returns a set of node positions that are supposedly just enough to satisfy all authentication + /// paths in the batch Merkle proof. + /// + /// See the following Merkle tree, where we build a batch authentication path for leaves [15,24] inclusively. + /// We'd only need nodes (12) and (6), because all the other nodes that would be needed in the authentication + /// path of any leaf can be obtained from just the leaves (which are public input). + /// If we were to build a batch authentication path for leaves [15,26] inclusively, then we'd need (6) alone, + /// because we could use nodes (25) and (26) to build (12), to be combined with (11) to obtain (5). + /// + /// 0 + /// 1 2 + /// 3 4 5 6 + /// 7 8 9 10 11 12 13 14 + /// 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 + /// + /// leaf_positions is a list of leaves to create a batch inclusion proof for. + /// For the example above, it would be [15, 16, 17, 18, 19, 20, 21, 22, 23, 24] + /// leaf_positions need not be continuous, but the resulting auth_set becomes the smallest when so. + fn get_batch_auth_path_positions(&self, leaf_positions: &[NodePos]) -> Vec { + let mut auth_set = HashSet::::new(); + // Add all the leaves to the set of obtainable nodes, because we already have them. + let mut obtainable_nodes_by_level: HashSet = + leaf_positions.iter().cloned().collect(); + + // Iterate levels starting from the leaves up to the root + for _ in (1..self.levels()).rev() { + let mut parent_level_obtainable_positions = HashSet::new(); + for pos in &obtainable_nodes_by_level { + let sibling_pos = get_sibling_pos(*pos); + let sibling_is_obtainable = obtainable_nodes_by_level.contains(&sibling_pos) + || auth_set.contains(&sibling_pos); + if !sibling_is_obtainable { + auth_set.insert(sibling_pos); } - pos = get_parent_pos(pos); + parent_level_obtainable_positions.insert(get_parent_pos(*pos)); } - } - - Ok(()) - } - - fn add_to_auth_map_if_not_contains<'a>( - &'a self, - auth_map: &mut HashMap, - pos: NodePos, - ) -> Result { - let Some(node) = self.nodes.get(pos) else { - return Err(Error::OutOfBounds); - }; - if auth_map.contains_key(&pos) { - return Ok(false); + obtainable_nodes_by_level = parent_level_obtainable_positions; } - auth_map.insert(pos, node); + auth_set.into_iter().collect() + } - Ok(true) + fn levels(&self) -> usize { + (self.nodes_len() as f32).log2().ceil() as usize } } #[cfg(test)] mod tests { - use std::collections::HashSet; - use super::*; use lambdaworks_math::field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}; @@ -148,7 +195,6 @@ mod tests { const MODULUS: u64 = 13; type U64PF = U64PrimeField; type FE = FieldElement; - type Node = > as IsMerkleTreeBackend>::Node; type TestTree = MerkleTree>; #[test] // expected | 10 | 3 | 7 | 1 | 2 | 3 | 4 | @@ -169,49 +215,4 @@ mod tests { let merkle_tree = TestTree::build(&values); assert_eq!(merkle_tree.root, FE::new(8)); } - - fn print_indices(tree_length: usize, mark_indices: HashSet) { - let depth = (tree_length as f64).log2().ceil() as usize; - let mut index = 0; - - for i in 0..depth { - let elements_at_this_depth = 2usize.pow(i as u32); - let padding = 2usize.pow((depth - i) as u32 + 1); - - for _ in 0..elements_at_this_depth { - if index >= tree_length { - continue; - } - if mark_indices.contains(&index) { - print!("{:width$}.", index, width = padding - 1); - } else { - print!("{:width$}", index, width = padding); - } - index += 1; - } - println!(); - } - } - - #[test] - fn build_auth_map() { - let leaf_values: Vec = (1..u64::pow(2, 4)).map(FE::new).collect(); - let merkle_tree = TestTree::build(&leaf_values); - - print_indices(merkle_tree.nodes.len(), HashSet::new()); - - let nodes_len = merkle_tree.nodes.len(); - let first_leaf_pos = nodes_len / 2; - let mut leaf_positions: Vec<_> = (0..leaf_values.len()) - .map(|i| (i + first_leaf_pos)) - .collect(); - - // Build an authentication map for the first 10 leaves - - let mut auth_map: HashMap = HashMap::new(); - TestTree::populate_auth_map(&merkle_tree, &mut auth_map, &mut leaf_positions[..10]) - .unwrap(); - - print_indices(merkle_tree.nodes.len(), auth_map.keys().cloned().collect()); - } } diff --git a/crypto/src/merkle_tree/mod.rs b/crypto/src/merkle_tree/mod.rs index 5fee9b7c8..8f2f57773 100644 --- a/crypto/src/merkle_tree/mod.rs +++ b/crypto/src/merkle_tree/mod.rs @@ -1,4 +1,5 @@ pub mod backends; +pub mod batch_proof; pub mod merkle; pub mod proof; #[cfg(test)] diff --git a/crypto/src/merkle_tree/proof.rs b/crypto/src/merkle_tree/proof.rs index 788ce9d4e..ce5bcbb96 100644 --- a/crypto/src/merkle_tree/proof.rs +++ b/crypto/src/merkle_tree/proof.rs @@ -123,7 +123,7 @@ mod tests { { let values: Vec = (1..6).map(FE::new).collect(); let merkle_tree = MerkleTree::>::build(&values); - let proof = &merkle_tree.get_proof_by_pos(1).unwrap(); + let proof = &merkle_tree.get_proof(1).unwrap(); assert_merkle_path(&proof.merkle_path, &[FE::new(2), FE::new(1), FE::new(1)]); assert!(proof.verify::>(&merkle_tree.root, 1, &FE::new(2))); } @@ -143,7 +143,7 @@ mod tests { fn create_a_merkle_tree_with_10000_elements_and_verify_that_an_element_is_part_of_it() { let values: Vec = (1..10000).map(Ecgfp5FE::new).collect(); let merkle_tree = TestMerkleTreeEcgfp::build(&values); - let proof = merkle_tree.get_proof_by_pos(9349).unwrap(); + let proof = merkle_tree.get_proof(9349).unwrap(); assert!(proof.verify::>(&merkle_tree.root, 9349, &Ecgfp5FE::new(9350))); } diff --git a/examples/merkle-tree-cli/src/main.rs b/examples/merkle-tree-cli/src/main.rs index b23b3f5e1..c9bf6d837 100644 --- a/examples/merkle-tree-cli/src/main.rs +++ b/examples/merkle-tree-cli/src/main.rs @@ -47,7 +47,7 @@ fn generate_merkle_proof(tree_path: String, pos: usize) -> Result<(), io::Error> let values: Vec = load_tree_values(&tree_path)?; let merkle_tree = MerkleTree::>::build(&values); - let Some(proof) = merkle_tree.get_proof_by_pos(pos) else { + let Some(proof) = merkle_tree.get_proof(pos) else { return Err(io::Error::new(io::ErrorKind::Other, "Index out of bounds")); }; diff --git a/provers/stark/src/fri/mod.rs b/provers/stark/src/fri/mod.rs index a841a8aea..7bac8b787 100644 --- a/provers/stark/src/fri/mod.rs +++ b/provers/stark/src/fri/mod.rs @@ -92,7 +92,7 @@ where for layer in fri_layers { // symmetric element let evaluation_sym = layer.evaluation[index ^ 1].clone(); - let auth_path_sym = layer.merkle_tree.get_proof_by_pos(index >> 1).unwrap(); + let auth_path_sym = layer.merkle_tree.get_proof(index >> 1).unwrap(); layers_evaluations_sym.push(evaluation_sym); layers_auth_paths_sym.push(auth_path_sym); diff --git a/provers/stark/src/prover.rs b/provers/stark/src/prover.rs index 7a5a4210e..cc16519ec 100644 --- a/provers/stark/src/prover.rs +++ b/provers/stark/src/prover.rs @@ -679,9 +679,7 @@ pub trait IsStarkProver { FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, { - let proof = composition_poly_merkle_tree - .get_proof_by_pos(index) - .unwrap(); + let proof = composition_poly_merkle_tree.get_proof(index).unwrap(); let lde_composition_poly_parts_evaluation: Vec<_> = lde_composition_poly_evaluations .iter() @@ -729,8 +727,8 @@ pub trait IsStarkProver { let index = challenge * 2; let index_sym = challenge * 2 + 1; PolynomialOpenings { - proof: tree.get_proof_by_pos(index).unwrap(), - proof_sym: tree.get_proof_by_pos(index_sym).unwrap(), + proof: tree.get_proof(index).unwrap(), + proof_sym: tree.get_proof(index_sym).unwrap(), evaluations: lde_trace .get_row(reverse_index(index, domain_size as u64)) .to_vec(), From d74a85bc1e048f6f1a47cb95d42a4f7a8a630431 Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Wed, 13 Mar 2024 23:36:16 +0300 Subject: [PATCH 04/22] different test cases functional --- crypto/src/merkle_tree/batch_proof.rs | 94 +++++++++++++++++---------- crypto/src/merkle_tree/merkle.rs | 22 +++++-- crypto/src/merkle_tree/utils.rs | 3 + 3 files changed, 77 insertions(+), 42 deletions(-) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index 189987083..443344a5b 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -193,57 +193,81 @@ mod tests { // So if leaf_values = [1,2,3,4], then actual leaf values will be [2,4,6,8], making the root 20. #[test] fn batch_proof_pen_and_paper_example() { - let leaf_values: Vec = (1..u64::pow(2, 2) + 1).map(Ecgfp5FE::new).collect(); + let mut leaf_values: Vec = (1..u64::pow(2, 2) + 1).map(Ecgfp5FE::new).collect(); let merkle_tree: MerkleTree = TestMerkleTreeEcgfp::build(&leaf_values); + leaf_values = merkle_tree.leaves().to_vec(); let nodes_len = merkle_tree.nodes_len(); let first_leaf_pos = nodes_len / 2; // Leaves to prove inclusion for - let leaf_indices = [0, 3]; - let leaf_positions: Vec = leaf_indices + let proven_leaf_indices = [0, 3]; + let proven_leaf_positions: Vec = proven_leaf_indices .iter() .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); // Build an authentication map for the first 10 leaves - let batch_proof = merkle_tree.get_batch_proof(&leaf_positions).unwrap(); + let batch_proof = merkle_tree.get_batch_proof(&proven_leaf_positions).unwrap(); - let leaves: HashMap = leaf_positions + let leaves: HashMap = proven_leaf_positions .iter() - .map(|pos| { - ( - *pos, - TestBackend::hash_data(&leaf_values[*pos - first_leaf_pos].clone()), - ) - }) + .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos].clone())) .collect(); assert!(batch_proof.verify::(merkle_tree.root, leaves)); } - // #[test] - // fn create_a_merkle_tree_with_10000_elements_and_verify_that_an_element_is_part_of_it() { - // let values: Vec = (1..10000).map(Ecgfp5FE::new).collect(); - // let merkle_tree = TestMerkleTreeEcgfp::build(&values); - - // let proven_leaf_range = 4..usize::pow(2, 8); - - // let batch_proof = merkle_tree - // .get_batch_proof(&proven_leaf_range.clone().collect::>()) - // .unwrap(); - - // let leaves = HashMap::from_iter( - // merkle_tree - // .get_batch_with_positions( - // &proven_leaf_range - // .map(|e| NodePos::from(e)) - // .collect::>(), - // ) - // .iter() - // .map(|(pos, elem)| (*pos, elem.clone())), - // ); - - // assert!(batch_proof.verify::>(merkle_tree.root, leaves)); - // } + #[test] + fn batch_proof_big_tree_one_leaf() { + let mut leaf_values: Vec = (1..u64::pow(2, 16) + 1).map(Ecgfp5FE::new).collect(); + let merkle_tree: MerkleTree = TestMerkleTreeEcgfp::build(&leaf_values); + leaf_values = merkle_tree.leaves().to_vec(); + + let nodes_len = merkle_tree.nodes_len(); + let first_leaf_pos = nodes_len / 2; + + // Only prove one of the leaves + let proven_leaf_indices = [76]; + let proven_leaf_positions: Vec = proven_leaf_indices + .iter() + .map(|leaf_index| leaf_index + first_leaf_pos) + .collect(); + + // Build an authentication map for the first 10 leaves + let batch_proof = merkle_tree.get_batch_proof(&proven_leaf_positions).unwrap(); + + let leaves: HashMap = proven_leaf_positions + .iter() + .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos].clone())) + .collect(); + + assert!(batch_proof.verify::(merkle_tree.root, leaves)); + } + + #[test] + fn create_a_merkle_tree_with_10000_elements_and_verify_that_a_series_of_elements_belong_to_it() + { + let mut leaf_values: Vec = (1..10000).map(Ecgfp5FE::new).collect(); + let merkle_tree = TestMerkleTreeEcgfp::build(&leaf_values); + leaf_values = merkle_tree.leaves().to_vec(); + + let nodes_len = merkle_tree.nodes_len(); + let first_leaf_pos = nodes_len / 2; + + let proven_leaf_indices = [0].iter(); + let proven_leaf_positions: Vec = proven_leaf_indices + .clone() + .map(|leaf_index| leaf_index + first_leaf_pos) + .collect(); + + let batch_proof = merkle_tree.get_batch_proof(&proven_leaf_positions).unwrap(); + + let leaves: HashMap = proven_leaf_positions + .iter() + .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos])) + .collect(); + + assert!(batch_proof.verify::(merkle_tree.root, leaves)); + } } diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index bf7e505d1..ed0a32f2b 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -72,8 +72,8 @@ where //Build the inner nodes of the tree build::(&mut nodes, leaves_len); - #[cfg(test)] - print_positions(nodes.len(), HashSet::new()); + // #[cfg(test)] + // print_positions(nodes.len(), HashSet::new()); MerkleTree { root: nodes[ROOT].clone(), @@ -81,6 +81,10 @@ where } } + pub fn leaves(&self) -> &[B::Node] { + &self.nodes[self.nodes_len() / 2..] + } + pub fn get_proof(&self, pos: NodePos) -> Option> { let first_leaf_index = self.nodes_len() / 2; let pos = pos + first_leaf_index; @@ -97,11 +101,11 @@ where /// pos_list need not be continuous, but the resulting proof becomes the smallest when so. pub fn get_batch_proof(&self, pos_list: &[NodePos]) -> Option> { let batch_auth_path_positions = self.get_batch_auth_path_positions(pos_list); - #[cfg(test)] - print_positions( - self.nodes_len(), - batch_auth_path_positions.iter().cloned().collect(), - ); + // #[cfg(test)] + // print_positions( + // self.nodes_len(), + // batch_auth_path_positions.iter().cloned().collect(), + // ); let batch_auth_path_nodes_iter = batch_auth_path_positions .iter() .map(|pos| (*pos, self.nodes[*pos].clone()).clone()); @@ -166,6 +170,10 @@ where let mut parent_level_obtainable_positions = HashSet::new(); for pos in &obtainable_nodes_by_level { let sibling_pos = get_sibling_pos(*pos); + if sibling_pos == *pos { + break; + } + let sibling_is_obtainable = obtainable_nodes_by_level.contains(&sibling_pos) || auth_set.contains(&sibling_pos); if !sibling_is_obtainable { diff --git a/crypto/src/merkle_tree/utils.rs b/crypto/src/merkle_tree/utils.rs index 43b07f230..5573b78ee 100644 --- a/crypto/src/merkle_tree/utils.rs +++ b/crypto/src/merkle_tree/utils.rs @@ -6,6 +6,9 @@ use rayon::prelude::*; pub fn get_sibling_pos(node_pos: usize) -> usize { if node_pos % 2 == 0 { + if node_pos == 0 { + return node_pos; + } node_pos - 1 } else { node_pos + 1 From 4e38ae01f7ca25e4981036ddfb6c35b34c04f3b1 Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Thu, 14 Mar 2024 00:01:23 +0300 Subject: [PATCH 05/22] get rid of serde code --- crypto/src/merkle_tree/batch_proof.rs | 121 +++++++------------------- 1 file changed, 33 insertions(+), 88 deletions(-) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index 443344a5b..62635af76 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -1,16 +1,12 @@ -use std::collections::HashMap; - -use alloc::vec::Vec; -#[cfg(feature = "alloc")] -use lambdaworks_math::traits::Serializable; #[cfg(feature = "parallel")] use rayon::prelude::*; +use std::collections::HashMap; -use lambdaworks_math::{errors::DeserializationError, traits::Deserializable}; - -use crate::merkle_tree::utils::get_parent_pos; - -use super::{merkle::NodePos, traits::IsMerkleTreeBackend, utils::get_sibling_pos}; +use super::{ + merkle::NodePos, + traits::IsMerkleTreeBackend, + utils::{get_parent_pos, get_sibling_pos}, +}; /// Stores a merkle path to some leaf. /// Internally, the necessary hashes are stored from root to leaf in the @@ -32,6 +28,7 @@ impl BatchProof { // Iterate the levels starting from the leaves, and build the upper level only using // the provided leaves and the auth map. + // Return true if the constructed root matches the given one. let mut current_level = hashed_leaves; loop { let mut parent_level = HashMap::::new(); @@ -79,52 +76,17 @@ impl BatchProof { } } -// #[cfg(feature = "alloc")] -// impl Serializable for BatchProof -// where -// T: Serializable + PartialEq + Eq, -// { -// fn serialize(&self) -> Vec { -// self.merkle_path -// .iter() -// .flat_map(|node| node.serialize()) -// .collect() -// } -// } - -// impl Deserializable for BatchProof -// where -// T: Deserializable + PartialEq + Eq, -// { -// fn deserialize(bytes: &[u8]) -> Result -// where -// Self: Sized, -// { -// let mut auth = Vec::new(); -// for elem in bytes[0..].chunks(8) { -// let node = T::deserialize(elem)?; -// auth.push(node); -// } -// Ok(Self { auth }) -// } -// } - #[cfg(test)] mod tests { - use std::collections::{HashMap, HashSet}; + use std::collections::HashMap; - #[cfg(feature = "alloc")] - use super::BatchProof; use alloc::vec::Vec; use lambdaworks_math::field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}; - #[cfg(feature = "alloc")] - use lambdaworks_math::traits::{Deserializable, Serializable}; use crate::merkle_tree::{ merkle::{MerkleTree, NodePos}, test_merkle::TestBackend as TB, - traits::IsMerkleTreeBackend, }; /// Small field useful for starks, sometimes called min i goldilocks @@ -134,48 +96,6 @@ mod tests { pub type Ecgfp5FE = FieldElement; pub type TestBackend = TB; pub type TestMerkleTreeEcgfp = MerkleTree; - #[cfg(feature = "alloc")] - pub type TestBatchProofEcgfp5 = BatchProof; - - const MODULUS: u64 = 13; - type U64PF = U64PrimeField; - type FE = FieldElement; - - // #[test] - // #[cfg(feature = "alloc")] - // fn serialize_batch_proof_and_deserialize_using_be_it_get_a_consistent_batch_proof() { - // let merkle_path = [Ecgfp5FE::new(2), Ecgfp5FE::new(1), Ecgfp5FE::new(1)].to_vec(); - // let original_batch_proof = TestBatchProofEcgfp5 { merkle_path }; - // let serialize_batch_proof = original_batch_proof.serialize(); - // let batch_proof: TestBatchProofEcgfp5 = - // BatchProof::deserialize(&serialize_batch_proof).unwrap(); - - // for (o_node, node) in original_batch_proof - // .merkle_path - // .iter() - // .zip(batch_proof.merkle_path) - // { - // assert_eq!(*o_node, node); - // } - // } - - // #[test] - // #[cfg(feature = "alloc")] - // fn serialize_batch_proof_and_deserialize_using_le_it_get_a_consistent_batch_proof() { - // let merkle_path = [Ecgfp5FE::new(2), Ecgfp5FE::new(1), Ecgfp5FE::new(1)].to_vec(); - // let original_batch_proof = TestBatchProofEcgfp5 { merkle_path }; - // let serialize_batch_proof = original_batch_proof.serialize(); - // let batch_proof: TestBatchProofEcgfp5 = - // BatchProof::deserialize(&serialize_batch_proof).unwrap(); - - // for (o_node, node) in original_batch_proof - // .merkle_path - // .iter() - // .zip(batch_proof.merkle_path) - // { - // assert_eq!(*o_node, node); - // } - // } // Creates following tree: // @@ -245,6 +165,31 @@ mod tests { assert!(batch_proof.verify::(merkle_tree.root, leaves)); } + #[test] + fn batch_proof_big_tree_many_leaves() { + let mut leaf_values: Vec = (1..u64::pow(2, 16)).map(Ecgfp5FE::new).collect(); + let merkle_tree: MerkleTree = TestMerkleTreeEcgfp::build(&leaf_values); + leaf_values = merkle_tree.leaves().to_vec(); + + let nodes_len = merkle_tree.nodes_len(); + let first_leaf_pos = nodes_len / 2; + + let proven_leaf_indices = usize::pow(2, 4) + 5..(usize::pow(2, 13) + 7); + let proven_leaf_positions: Vec = proven_leaf_indices + .map(|leaf_index| leaf_index + first_leaf_pos) + .collect(); + + // Build an authentication map for the first 10 leaves + let batch_proof = merkle_tree.get_batch_proof(&proven_leaf_positions).unwrap(); + + let leaves: HashMap = proven_leaf_positions + .iter() + .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos].clone())) + .collect(); + + assert!(batch_proof.verify::(merkle_tree.root, leaves)); + } + #[test] fn create_a_merkle_tree_with_10000_elements_and_verify_that_a_series_of_elements_belong_to_it() { From f4b1efa8bf2d09110911e56fa1e8149745d0b7f0 Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Thu, 14 Mar 2024 00:10:53 +0300 Subject: [PATCH 06/22] final cleansing --- crypto/src/merkle_tree/batch_proof.rs | 19 ++++----------- crypto/src/merkle_tree/merkle.rs | 34 ++------------------------- 2 files changed, 6 insertions(+), 47 deletions(-) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index 62635af76..f40d07497 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "parallel")] -use rayon::prelude::*; use std::collections::HashMap; use super::{ @@ -8,11 +6,6 @@ use super::{ utils::{get_parent_pos, get_sibling_pos}, }; -/// Stores a merkle path to some leaf. -/// Internally, the necessary hashes are stored from root to leaf in the -/// `merkle_path` field, in such a way that, if the merkle tree is of height `n`, the -/// `i`-th element of `merkle_path` is the sibling node in the `n - 1 - i`-th check -/// when verifying. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BatchProof { @@ -50,8 +43,7 @@ impl BatchProof { } else if self.auth.contains_key(&sibling_pos) { self.auth.get(&sibling_pos).unwrap() } else { - // no sibling to hash with! Return error. - panic!(); + panic!("Leaf with position {pos} has sibling {sibling_pos}, but it's not included in the auth map. "); }; let parent_node = B::hash_new_parent(node, sibling_node); @@ -97,7 +89,6 @@ mod tests { pub type TestBackend = TB; pub type TestMerkleTreeEcgfp = MerkleTree; - // Creates following tree: // // 20 // / \ @@ -106,11 +97,9 @@ mod tests { // 2 4 6 8 // // Proves inclusion of leaves whose indices are passed into 'leaf_indices' array. - // If it's [0, 3], then the test will create proof and verify inclusion of leaves with indices 0 and 3, - // that are, 2 and 8. - // - // The test uses a test backend whose hash function is just an element added to itself. - // So if leaf_values = [1,2,3,4], then actual leaf values will be [2,4,6,8], making the root 20. + // These leaf indices start from 0 for the first leaf, 2 in the example above. + // If leaf_indices is [0, 3], then the test will create proof and verify inclusion + // of leaves with indices 0 and 3, that are, 2 and 8. #[test] fn batch_proof_pen_and_paper_example() { let mut leaf_values: Vec = (1..u64::pow(2, 2) + 1).map(Ecgfp5FE::new).collect(); diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index ed0a32f2b..4de0f5f89 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -29,30 +29,6 @@ pub struct MerkleTree { nodes: Vec, } -#[cfg(test)] -fn print_positions(tree_length: usize, mark_positions: HashSet) { - let depth = (tree_length as f64).log2().ceil() as usize; - let mut index = 0; - - for i in 0..depth { - let elements_at_this_depth = 2usize.pow(i as u32); - let padding = 2usize.pow((depth - i) as u32 + 1); - - for _ in 0..elements_at_this_depth { - if index >= tree_length { - continue; - } - if mark_positions.contains(&index) { - print!("{:width$}.", index, width = padding - 1); - } else { - print!("{:width$}", index, width = padding); - } - index += 1; - } - println!(); - } -} - impl MerkleTree where B: IsMerkleTreeBackend, @@ -101,11 +77,7 @@ where /// pos_list need not be continuous, but the resulting proof becomes the smallest when so. pub fn get_batch_proof(&self, pos_list: &[NodePos]) -> Option> { let batch_auth_path_positions = self.get_batch_auth_path_positions(pos_list); - // #[cfg(test)] - // print_positions( - // self.nodes_len(), - // batch_auth_path_positions.iter().cloned().collect(), - // ); + let batch_auth_path_nodes_iter = batch_auth_path_positions .iter() .map(|pos| (*pos, self.nodes[*pos].clone()).clone()); @@ -170,9 +142,6 @@ where let mut parent_level_obtainable_positions = HashSet::new(); for pos in &obtainable_nodes_by_level { let sibling_pos = get_sibling_pos(*pos); - if sibling_pos == *pos { - break; - } let sibling_is_obtainable = obtainable_nodes_by_level.contains(&sibling_pos) || auth_set.contains(&sibling_pos); @@ -204,6 +173,7 @@ mod tests { type U64PF = U64PrimeField; type FE = FieldElement; type TestTree = MerkleTree>; + #[test] // expected | 10 | 3 | 7 | 1 | 2 | 3 | 4 | fn build_merkle_tree_from_a_power_of_two_list_of_values() { From 5aed2ab2730e13f9821952ce24111d270836ac29 Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Thu, 14 Mar 2024 00:26:54 +0300 Subject: [PATCH 07/22] self review --- crypto/src/merkle_tree/batch_proof.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index f40d07497..758f0340a 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -116,7 +116,6 @@ mod tests { .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); - // Build an authentication map for the first 10 leaves let batch_proof = merkle_tree.get_batch_proof(&proven_leaf_positions).unwrap(); let leaves: HashMap = proven_leaf_positions @@ -143,7 +142,6 @@ mod tests { .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); - // Build an authentication map for the first 10 leaves let batch_proof = merkle_tree.get_batch_proof(&proven_leaf_positions).unwrap(); let leaves: HashMap = proven_leaf_positions @@ -168,7 +166,6 @@ mod tests { .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); - // Build an authentication map for the first 10 leaves let batch_proof = merkle_tree.get_batch_proof(&proven_leaf_positions).unwrap(); let leaves: HashMap = proven_leaf_positions From c50380bb29edf3a981870be290942459741071ac Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Thu, 14 Mar 2024 16:51:57 +0300 Subject: [PATCH 08/22] clippy --- crypto/src/merkle_tree/batch_proof.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index 758f0340a..61a380696 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -120,7 +120,7 @@ mod tests { let leaves: HashMap = proven_leaf_positions .iter() - .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos].clone())) + .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos])) .collect(); assert!(batch_proof.verify::(merkle_tree.root, leaves)); @@ -146,7 +146,7 @@ mod tests { let leaves: HashMap = proven_leaf_positions .iter() - .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos].clone())) + .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos])) .collect(); assert!(batch_proof.verify::(merkle_tree.root, leaves)); @@ -170,7 +170,7 @@ mod tests { let leaves: HashMap = proven_leaf_positions .iter() - .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos].clone())) + .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos])) .collect(); assert!(batch_proof.verify::(merkle_tree.root, leaves)); From eebb6b8a6a5b58531eafc2ff835623f7412b560e Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Thu, 14 Mar 2024 17:09:16 +0300 Subject: [PATCH 09/22] self review + renamings --- crypto/src/merkle_tree/batch_proof.rs | 102 +++++++++++++------------- crypto/src/merkle_tree/merkle.rs | 8 +- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index 61a380696..94ab9be80 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -96,109 +96,105 @@ mod tests { // / \ / \ // 2 4 6 8 // - // Proves inclusion of leaves whose indices are passed into 'leaf_indices' array. - // These leaf indices start from 0 for the first leaf, 2 in the example above. + // Proves inclusion of leaves whose indices are passed into 'proven_leaf_indices' + // array. These leaf indices start from 0 for the first leaf, 2 in the example above. // If leaf_indices is [0, 3], then the test will create proof and verify inclusion // of leaves with indices 0 and 3, that are, 2 and 8. #[test] fn batch_proof_pen_and_paper_example() { - let mut leaf_values: Vec = (1..u64::pow(2, 2) + 1).map(Ecgfp5FE::new).collect(); - let merkle_tree: MerkleTree = TestMerkleTreeEcgfp::build(&leaf_values); - leaf_values = merkle_tree.leaves().to_vec(); + let leaves_values: Vec = (1..u64::pow(2, 2) + 1).map(Ecgfp5FE::new).collect(); + let merkle_tree: MerkleTree = TestMerkleTreeEcgfp::build(&leaves_values); - let nodes_len = merkle_tree.nodes_len(); - let first_leaf_pos = nodes_len / 2; - - // Leaves to prove inclusion for - let proven_leaf_indices = [0, 3]; - let proven_leaf_positions: Vec = proven_leaf_indices + let proven_leaves_indices = [0, 3]; + let first_leaf_pos = merkle_tree.nodes_len() / 2; + let proven_leaves_positions: Vec = proven_leaves_indices .iter() .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); - let batch_proof = merkle_tree.get_batch_proof(&proven_leaf_positions).unwrap(); + let batch_proof = merkle_tree + .get_batch_proof(&proven_leaves_positions) + .unwrap(); - let leaves: HashMap = proven_leaf_positions + let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() - .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos])) + .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) .collect(); - assert!(batch_proof.verify::(merkle_tree.root, leaves)); + assert!(batch_proof.verify::(merkle_tree.root, proven_leaves_values_hashed)); } #[test] fn batch_proof_big_tree_one_leaf() { - let mut leaf_values: Vec = (1..u64::pow(2, 16) + 1).map(Ecgfp5FE::new).collect(); - let merkle_tree: MerkleTree = TestMerkleTreeEcgfp::build(&leaf_values); - leaf_values = merkle_tree.leaves().to_vec(); - - let nodes_len = merkle_tree.nodes_len(); - let first_leaf_pos = nodes_len / 2; + let leaves_values: Vec = (1..u64::pow(2, 16) + 1).map(Ecgfp5FE::new).collect(); + let merkle_tree: MerkleTree = TestMerkleTreeEcgfp::build(&leaves_values); - // Only prove one of the leaves - let proven_leaf_indices = [76]; - let proven_leaf_positions: Vec = proven_leaf_indices + let proven_leaves_indices = [76]; // Only prove one of the leaves + let first_leaf_pos = merkle_tree.nodes_len() / 2; + let proven_leaves_positions: Vec = proven_leaves_indices .iter() .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); - let batch_proof = merkle_tree.get_batch_proof(&proven_leaf_positions).unwrap(); + let batch_proof = merkle_tree + .get_batch_proof(&proven_leaves_positions) + .unwrap(); - let leaves: HashMap = proven_leaf_positions + let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() - .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos])) + .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) .collect(); - assert!(batch_proof.verify::(merkle_tree.root, leaves)); + assert!(batch_proof.verify::(merkle_tree.root, proven_leaves_values_hashed)); } #[test] fn batch_proof_big_tree_many_leaves() { - let mut leaf_values: Vec = (1..u64::pow(2, 16)).map(Ecgfp5FE::new).collect(); - let merkle_tree: MerkleTree = TestMerkleTreeEcgfp::build(&leaf_values); - leaf_values = merkle_tree.leaves().to_vec(); - - let nodes_len = merkle_tree.nodes_len(); - let first_leaf_pos = nodes_len / 2; - - let proven_leaf_indices = usize::pow(2, 4) + 5..(usize::pow(2, 13) + 7); - let proven_leaf_positions: Vec = proven_leaf_indices + // Just add -18 to make the test case a little more complex + let all_leaves_values: Vec = + (1..u64::pow(2, 16) - 18).map(Ecgfp5FE::new).collect(); + let merkle_tree: MerkleTree = TestMerkleTreeEcgfp::build(&all_leaves_values); + + let proven_leaves_indices = usize::pow(2, 4) + 5..(usize::pow(2, 13) + 7); + let first_leaf_pos = merkle_tree.nodes_len() / 2; + let proven_leaves_positions: Vec = proven_leaves_indices .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); - let batch_proof = merkle_tree.get_batch_proof(&proven_leaf_positions).unwrap(); + let batch_proof = merkle_tree + .get_batch_proof(&proven_leaves_positions) + .unwrap(); - let leaves: HashMap = proven_leaf_positions + let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() - .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos])) + .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) .collect(); - assert!(batch_proof.verify::(merkle_tree.root, leaves)); + assert!(batch_proof.verify::(merkle_tree.root, proven_leaves_values_hashed)); } #[test] fn create_a_merkle_tree_with_10000_elements_and_verify_that_a_series_of_elements_belong_to_it() { - let mut leaf_values: Vec = (1..10000).map(Ecgfp5FE::new).collect(); - let merkle_tree = TestMerkleTreeEcgfp::build(&leaf_values); - leaf_values = merkle_tree.leaves().to_vec(); - - let nodes_len = merkle_tree.nodes_len(); - let first_leaf_pos = nodes_len / 2; + let all_leaves_values: Vec = (1..10000).map(Ecgfp5FE::new).collect(); + let merkle_tree = TestMerkleTreeEcgfp::build(&all_leaves_values); - let proven_leaf_indices = [0].iter(); - let proven_leaf_positions: Vec = proven_leaf_indices + let proven_leaves_indices = [0].iter(); + let first_leaf_pos = merkle_tree.nodes_len() / 2; + let proven_leaves_positions: Vec = proven_leaves_indices .clone() .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); - let batch_proof = merkle_tree.get_batch_proof(&proven_leaf_positions).unwrap(); + let batch_proof = merkle_tree + .get_batch_proof(&proven_leaves_positions) + .unwrap(); - let leaves: HashMap = proven_leaf_positions + let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() - .map(|pos| (*pos, leaf_values[*pos - first_leaf_pos])) + .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) .collect(); - assert!(batch_proof.verify::(merkle_tree.root, leaves)); + assert!(batch_proof.verify::(merkle_tree.root, proven_leaves_values_hashed)); } } diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index 4de0f5f89..22b0857eb 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -57,8 +57,12 @@ where } } - pub fn leaves(&self) -> &[B::Node] { - &self.nodes[self.nodes_len() / 2..] + /// Returns the leaf at the given index. + /// leaf_index starts from 0 for the first leaf. In other words, don't use the overall position + /// of the leaf. + pub fn get_leaf(&self, leaf_index: usize) -> &B::Node { + let first_leaf_pos = self.nodes_len() / 2; + &self.nodes[leaf_index + first_leaf_pos] } pub fn get_proof(&self, pos: NodePos) -> Option> { From 86140f36175d8a4a880b0b73f44225b9a59664b9 Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Fri, 15 Mar 2024 19:05:58 +0300 Subject: [PATCH 10/22] clippy --- crypto/src/merkle_tree/merkle.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index 22b0857eb..ffd9a1423 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -58,11 +58,10 @@ where } /// Returns the leaf at the given index. - /// leaf_index starts from 0 for the first leaf. In other words, don't use the overall position - /// of the leaf. - pub fn get_leaf(&self, leaf_index: usize) -> &B::Node { + /// leaf_index starts from 0 for the first leaf. + pub fn get_leaf(&self, leaf_index: usize) -> B::Node { let first_leaf_pos = self.nodes_len() / 2; - &self.nodes[leaf_index + first_leaf_pos] + self.nodes[leaf_index + first_leaf_pos].clone() } pub fn get_proof(&self, pos: NodePos) -> Option> { From cbc781f6fb1fa60af4c7a593f44f7309f0b974cf Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Sun, 17 Mar 2024 22:24:51 +0300 Subject: [PATCH 11/22] add serde --- crypto/Cargo.toml | 1 + crypto/src/merkle_tree/batch_proof.rs | 39 +++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 78ccd9263..1f5fb1efc 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -14,6 +14,7 @@ sha2 = { version = "0.10", default-features = false } # Optional serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true } +bincode = { version = "2.0.0-rc.2", tag = "v2.0.0-rc.2", git = "https://github.com/bincode-org/bincode.git", features = ["derive", "alloc"] } rayon = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index 94ab9be80..56bd567f1 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -1,18 +1,47 @@ -use std::collections::HashMap; - use super::{ merkle::NodePos, traits::IsMerkleTreeBackend, utils::{get_parent_pos, get_sibling_pos}, }; +use lambdaworks_math::{errors::DeserializationError, traits::Deserializable}; +use std::collections::HashMap; -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +// extern crate bincode; +use bincode::{config, Decode, Encode}; + +#[derive(Debug, Clone, Encode, Decode, PartialEq)] pub struct BatchProof { pub auth: HashMap, } -impl BatchProof { +impl Deserializable for BatchProof +where + T: Deserializable + PartialEq + Eq + Encode + Decode, +{ + fn deserialize(bytes: &[u8]) -> Result + where + Self: Sized, + { + let (decoded, _): (BatchProof, usize) = + bincode::decode_from_slice(&bytes[..], config::standard()).unwrap(); + Ok(decoded) + } +} + +impl BatchProof +where + T: PartialEq + Eq + Encode + Decode, +{ + // No available 'Serializable' trait as per now + pub fn serialize(&self) -> Vec { + bincode::encode_to_vec(&self, config::standard()).unwrap() + } +} + +impl BatchProof +where + T: PartialEq + Eq, +{ pub fn verify(&self, root_hash: B::Node, hashed_leaves: HashMap) -> bool where B: IsMerkleTreeBackend, From 37d4a35a64608e42aac9269d1ab9731e1fe166db Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Mon, 18 Mar 2024 01:19:58 +0300 Subject: [PATCH 12/22] add serde w/ bincode --- crypto/Cargo.toml | 4 +- crypto/src/merkle_tree/batch_proof.rs | 98 ++++++++++--------- crypto/src/merkle_tree/merkle.rs | 15 ++- math/src/field/fields/u64_goldilocks_field.rs | 12 ++- 4 files changed, 71 insertions(+), 58 deletions(-) diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 1f5fb1efc..a11c60796 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -8,13 +8,13 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lambdaworks-math = { workspace = true, features = ["alloc"] } +lambdaworks-math = { workspace = true, default-features = true, features = ["alloc", "lambdaworks-serde-binary"] } sha3 = { version = "0.10", default-features = false } sha2 = { version = "0.10", default-features = false } # Optional serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true } -bincode = { version = "2.0.0-rc.2", tag = "v2.0.0-rc.2", git = "https://github.com/bincode-org/bincode.git", features = ["derive", "alloc"] } +bincode = { version = "2.0.0-rc.2", tag = "v2.0.0-rc.2", git = "https://github.com/bincode-org/bincode.git", features = ['serde'] } rayon = { version = "1.8.0", optional = true } [dev-dependencies] diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index 56bd567f1..feee0747b 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -1,40 +1,33 @@ use super::{ - merkle::NodePos, traits::IsMerkleTreeBackend, utils::{get_parent_pos, get_sibling_pos}, }; -use lambdaworks_math::{errors::DeserializationError, traits::Deserializable}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::collections::HashMap; -// extern crate bincode; -use bincode::{config, Decode, Encode}; - -#[derive(Debug, Clone, Encode, Decode, PartialEq)] -pub struct BatchProof { - pub auth: HashMap, -} - -impl Deserializable for BatchProof +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BatchProof where - T: Deserializable + PartialEq + Eq + Encode + Decode, + T: PartialEq + Eq, { - fn deserialize(bytes: &[u8]) -> Result - where - Self: Sized, - { - let (decoded, _): (BatchProof, usize) = - bincode::decode_from_slice(&bytes[..], config::standard()).unwrap(); - Ok(decoded) - } + pub auth: HashMap, } +#[cfg(feature = "serde")] impl BatchProof where - T: PartialEq + Eq + Encode + Decode, + T: PartialEq + Eq + Serialize + for<'a> Deserialize<'a>, { - // No available 'Serializable' trait as per now pub fn serialize(&self) -> Vec { - bincode::encode_to_vec(&self, config::standard()).unwrap() + bincode::serde::encode_to_vec(&self, bincode::config::standard()).unwrap() + } + + pub fn deserialize(bytes: &[u8]) -> Self { + let (decoded, _): (BatchProof, usize) = + bincode::serde::decode_from_slice(&bytes[..], bincode::config::standard()).unwrap(); + decoded } } @@ -42,7 +35,7 @@ impl BatchProof where T: PartialEq + Eq, { - pub fn verify(&self, root_hash: B::Node, hashed_leaves: HashMap) -> bool + pub fn verify(&self, root_hash: B::Node, hashed_leaves: HashMap) -> bool where B: IsMerkleTreeBackend, { @@ -53,7 +46,7 @@ where // Return true if the constructed root matches the given one. let mut current_level = hashed_leaves; loop { - let mut parent_level = HashMap::::new(); + let mut parent_level = HashMap::::new(); for (pos, node) in current_level.iter() { // Levels are expected to have tuples of nodes. If the first one was @@ -100,19 +93,13 @@ where #[cfg(test)] mod tests { - use std::collections::HashMap; - + // #[cfg(feature = "alloc")] + use crate::merkle_tree::{merkle::MerkleTree, test_merkle::TestBackend as TB}; use alloc::vec::Vec; - use lambdaworks_math::field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}; - - use crate::merkle_tree::{ - merkle::{MerkleTree, NodePos}, - test_merkle::TestBackend as TB, - }; + use lambdaworks_math::field::{element::*, fields::u64_prime_field::U64PrimeField}; + use std::collections::HashMap; - /// Small field useful for starks, sometimes called min i goldilocks - /// Used in miden and winterfell - // This field shouldn't be defined inside the merkle tree module + /// Goldilocks pub type Ecgfp5 = U64PrimeField<0xFFFF_FFFF_0000_0001_u64>; pub type Ecgfp5FE = FieldElement; pub type TestBackend = TB; @@ -136,7 +123,7 @@ mod tests { let proven_leaves_indices = [0, 3]; let first_leaf_pos = merkle_tree.nodes_len() / 2; - let proven_leaves_positions: Vec = proven_leaves_indices + let proven_leaves_positions: Vec = proven_leaves_indices .iter() .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); @@ -145,7 +132,7 @@ mod tests { .get_batch_proof(&proven_leaves_positions) .unwrap(); - let proven_leaves_values_hashed: HashMap = proven_leaves_positions + let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) .collect(); @@ -160,7 +147,7 @@ mod tests { let proven_leaves_indices = [76]; // Only prove one of the leaves let first_leaf_pos = merkle_tree.nodes_len() / 2; - let proven_leaves_positions: Vec = proven_leaves_indices + let proven_leaves_positions: Vec = proven_leaves_indices .iter() .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); @@ -169,7 +156,7 @@ mod tests { .get_batch_proof(&proven_leaves_positions) .unwrap(); - let proven_leaves_values_hashed: HashMap = proven_leaves_positions + let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) .collect(); @@ -186,7 +173,7 @@ mod tests { let proven_leaves_indices = usize::pow(2, 4) + 5..(usize::pow(2, 13) + 7); let first_leaf_pos = merkle_tree.nodes_len() / 2; - let proven_leaves_positions: Vec = proven_leaves_indices + let proven_leaves_positions: Vec = proven_leaves_indices .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); @@ -194,7 +181,7 @@ mod tests { .get_batch_proof(&proven_leaves_positions) .unwrap(); - let proven_leaves_values_hashed: HashMap = proven_leaves_positions + let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) .collect(); @@ -210,7 +197,7 @@ mod tests { let proven_leaves_indices = [0].iter(); let first_leaf_pos = merkle_tree.nodes_len() / 2; - let proven_leaves_positions: Vec = proven_leaves_indices + let proven_leaves_positions: Vec = proven_leaves_indices .clone() .map(|leaf_index| leaf_index + first_leaf_pos) .collect(); @@ -219,11 +206,34 @@ mod tests { .get_batch_proof(&proven_leaves_positions) .unwrap(); - let proven_leaves_values_hashed: HashMap = proven_leaves_positions + let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) .collect(); assert!(batch_proof.verify::(merkle_tree.root, proven_leaves_values_hashed)); } + + #[cfg(feature = "serde")] + #[test] + fn proof_is_same_after_serde() { + let all_leaves_values: Vec = (1..10000).map(Ecgfp5FE::new).collect(); + let merkle_tree = TestMerkleTreeEcgfp::build(&all_leaves_values); + + let proven_leaves_indices = [0].iter(); + let first_leaf_pos = merkle_tree.nodes_len() / 2; + let proven_leaves_positions: Vec = proven_leaves_indices + .clone() + .map(|leaf_index| leaf_index + first_leaf_pos) + .collect(); + + let batch_proof = merkle_tree + .get_batch_proof(&proven_leaves_positions) + .unwrap(); + + let serialized = batch_proof.serialize(); + let batch_proof_2 = super::BatchProof::::deserialize(&serialized); + + assert_eq!(batch_proof, batch_proof_2); + } } diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index ffd9a1423..c0f275eb7 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -5,8 +5,7 @@ use alloc::vec::Vec; use super::{batch_proof::BatchProof, proof::Proof, traits::IsMerkleTreeBackend, utils::*}; -pub type NodePos = usize; -const ROOT: NodePos = 0; +const ROOT: usize = 0; #[derive(Debug)] pub enum Error { @@ -64,7 +63,7 @@ where self.nodes[leaf_index + first_leaf_pos].clone() } - pub fn get_proof(&self, pos: NodePos) -> Option> { + pub fn get_proof(&self, pos: usize) -> Option> { let first_leaf_index = self.nodes_len() / 2; let pos = pos + first_leaf_index; let Ok(merkle_path) = self.build_merkle_path(pos) else { @@ -78,7 +77,7 @@ where /// /// pos_list is a list of leaf positions (within all tree) to create a batch inclusion proof for. /// pos_list need not be continuous, but the resulting proof becomes the smallest when so. - pub fn get_batch_proof(&self, pos_list: &[NodePos]) -> Option> { + pub fn get_batch_proof(&self, pos_list: &[usize]) -> Option> { let batch_auth_path_positions = self.get_batch_auth_path_positions(pos_list); let batch_auth_path_nodes_iter = batch_auth_path_positions @@ -97,7 +96,7 @@ where /// Builds and returns a proof of inclusion for the leaf whose position is passed as an argument. /// /// pos parameter is the index in overall Merkle tree, including the inner nodes - fn build_merkle_path(&self, pos: NodePos) -> Result, Error> { + fn build_merkle_path(&self, pos: usize) -> Result, Error> { let mut merkle_path = Vec::new(); let mut pos = pos; @@ -134,10 +133,10 @@ where /// leaf_positions is a list of leaves to create a batch inclusion proof for. /// For the example above, it would be [15, 16, 17, 18, 19, 20, 21, 22, 23, 24] /// leaf_positions need not be continuous, but the resulting auth_set becomes the smallest when so. - fn get_batch_auth_path_positions(&self, leaf_positions: &[NodePos]) -> Vec { - let mut auth_set = HashSet::::new(); + fn get_batch_auth_path_positions(&self, leaf_positions: &[usize]) -> Vec { + let mut auth_set = HashSet::::new(); // Add all the leaves to the set of obtainable nodes, because we already have them. - let mut obtainable_nodes_by_level: HashSet = + let mut obtainable_nodes_by_level: HashSet = leaf_positions.iter().cloned().collect(); // Iterate levels starting from the leaves up to the root diff --git a/math/src/field/fields/u64_goldilocks_field.rs b/math/src/field/fields/u64_goldilocks_field.rs index 5c1a29d3c..1d9f5b93d 100644 --- a/math/src/field/fields/u64_goldilocks_field.rs +++ b/math/src/field/fields/u64_goldilocks_field.rs @@ -26,26 +26,30 @@ impl Goldilocks64Field { impl ByteConversion for u64 { #[cfg(feature = "alloc")] fn to_bytes_be(&self) -> alloc::vec::Vec { - unimplemented!() + self.to_be_bytes().to_vec() } #[cfg(feature = "alloc")] fn to_bytes_le(&self) -> alloc::vec::Vec { - unimplemented!() + self.to_le_bytes().to_vec() } fn from_bytes_be(_bytes: &[u8]) -> Result where Self: Sized, { - unimplemented!() + Ok(u64::from_be_bytes(_bytes.try_into().expect( + "Tried to create a u64 from the byte array but its size was not 8", + ))) } fn from_bytes_le(_bytes: &[u8]) -> Result where Self: Sized, { - unimplemented!() + Ok(u64::from_le_bytes(_bytes.try_into().expect( + "Tried to create a u64 from the byte array but its size was not 8", + ))) } } From 6a364e5005966ab79ffaace9001d49743c88b2b4 Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Mon, 18 Mar 2024 02:21:21 +0300 Subject: [PATCH 13/22] remove unnecessary default-features --- crypto/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index a11c60796..80307fb0d 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -8,7 +8,7 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lambdaworks-math = { workspace = true, default-features = true, features = ["alloc", "lambdaworks-serde-binary"] } +lambdaworks-math = { workspace = true, features = ["alloc", "lambdaworks-serde-binary"] } sha3 = { version = "0.10", default-features = false } sha2 = { version = "0.10", default-features = false } From 985ccbd98fa611f956c40531c055374bffece82b Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Mon, 18 Mar 2024 16:03:12 +0300 Subject: [PATCH 14/22] clippy --- crypto/src/merkle_tree/batch_proof.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index feee0747b..4a3ef2fbc 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -21,12 +21,12 @@ where T: PartialEq + Eq + Serialize + for<'a> Deserialize<'a>, { pub fn serialize(&self) -> Vec { - bincode::serde::encode_to_vec(&self, bincode::config::standard()).unwrap() + bincode::serde::encode_to_vec(self, bincode::config::standard()).unwrap() } pub fn deserialize(bytes: &[u8]) -> Self { let (decoded, _): (BatchProof, usize) = - bincode::serde::decode_from_slice(&bytes[..], bincode::config::standard()).unwrap(); + bincode::serde::decode_from_slice(bytes, bincode::config::standard()).unwrap(); decoded } } From 9781478c2d6692323db30dd6d69a41f6449b9715 Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Tue, 19 Mar 2024 21:16:23 +0300 Subject: [PATCH 15/22] clippy --- crypto/src/merkle_tree/batch_proof.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index 4a3ef2fbc..7587e9ce5 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -134,7 +134,7 @@ mod tests { let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() - .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) + .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos))) .collect(); assert!(batch_proof.verify::(merkle_tree.root, proven_leaves_values_hashed)); @@ -158,7 +158,7 @@ mod tests { let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() - .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) + .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos))) .collect(); assert!(batch_proof.verify::(merkle_tree.root, proven_leaves_values_hashed)); @@ -183,7 +183,7 @@ mod tests { let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() - .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) + .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos))) .collect(); assert!(batch_proof.verify::(merkle_tree.root, proven_leaves_values_hashed)); @@ -208,7 +208,7 @@ mod tests { let proven_leaves_values_hashed: HashMap = proven_leaves_positions .iter() - .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos).clone())) + .map(|pos| (*pos, merkle_tree.get_leaf(*pos - first_leaf_pos))) .collect(); assert!(batch_proof.verify::(merkle_tree.root, proven_leaves_values_hashed)); From 93c8a30fe038243d2e697c158726facbea58c194 Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Wed, 20 Mar 2024 18:48:32 +0300 Subject: [PATCH 16/22] clippy again --- crypto/src/merkle_tree/batch_proof.rs | 1 + crypto/src/merkle_tree/merkle.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index 7587e9ce5..cc2bb1d74 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -4,6 +4,7 @@ use super::{ }; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +extern crate std; use std::collections::HashMap; #[derive(Debug, Clone, PartialEq)] diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index c0f275eb7..7561f61d3 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -1,4 +1,6 @@ use core::fmt::Display; + +extern crate std; use std::collections::HashSet; use alloc::vec::Vec; @@ -84,7 +86,7 @@ where .iter() .map(|pos| (*pos, self.nodes[*pos].clone()).clone()); - Some(BatchProof { + Some(BatchProof:: { auth: batch_auth_path_nodes_iter.collect(), }) } From cc6d87de65ccc27fe91b67feffdcb6966ca19c5d Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Wed, 20 Mar 2024 18:49:01 +0300 Subject: [PATCH 17/22] clippy again --- crypto/src/merkle_tree/merkle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index 7561f61d3..e5efe22a2 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -86,7 +86,7 @@ where .iter() .map(|pos| (*pos, self.nodes[*pos].clone()).clone()); - Some(BatchProof:: { + Some(BatchProof:: { auth: batch_auth_path_nodes_iter.collect(), }) } From 1ab96814c7bd8218c6b038d52925c5deca03bc7d Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Thu, 4 Apr 2024 02:15:19 +0300 Subject: [PATCH 18/22] clippy again --- crypto/src/merkle_tree/batch_proof.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index cc2bb1d74..8ba74aa5e 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -2,6 +2,7 @@ use super::{ traits::IsMerkleTreeBackend, utils::{get_parent_pos, get_sibling_pos}, }; +use alloc::vec::Vec; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; extern crate std; From 3e2c9c1a6d28cea3f6d6c373b5cd71b1a11e6192 Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Fri, 5 Apr 2024 03:53:13 +0300 Subject: [PATCH 19/22] clippy again and again and again I'm sick of it --- crypto/src/merkle_tree/batch_proof.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index 8ba74aa5e..cc2bb1d74 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -2,7 +2,6 @@ use super::{ traits::IsMerkleTreeBackend, utils::{get_parent_pos, get_sibling_pos}, }; -use alloc::vec::Vec; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; extern crate std; From e2c4998812ebd225e6ff2227a677c8393811683b Mon Sep 17 00:00:00 2001 From: Irfan Bozkurt Date: Mon, 29 Apr 2024 20:02:51 +0300 Subject: [PATCH 20/22] extern crate std --- crypto/src/merkle_tree/batch_proof.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crypto/src/merkle_tree/batch_proof.rs b/crypto/src/merkle_tree/batch_proof.rs index cc2bb1d74..95f825666 100644 --- a/crypto/src/merkle_tree/batch_proof.rs +++ b/crypto/src/merkle_tree/batch_proof.rs @@ -98,6 +98,7 @@ mod tests { use crate::merkle_tree::{merkle::MerkleTree, test_merkle::TestBackend as TB}; use alloc::vec::Vec; use lambdaworks_math::field::{element::*, fields::u64_prime_field::U64PrimeField}; + extern crate std; use std::collections::HashMap; /// Goldilocks From fdd86d0aa3774311db12f042d9b1fab36ee806ed Mon Sep 17 00:00:00 2001 From: Diego K <43053772+diegokingston@users.noreply.github.com> Date: Tue, 1 Oct 2024 16:39:56 -0300 Subject: [PATCH 21/22] Update merkle.rs --- crypto/src/merkle_tree/merkle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index c22eea4de..a3599e882 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -58,7 +58,7 @@ where MerkleTree { root: nodes[ROOT].clone(), nodes, - }) + } } /// Returns the leaf at the given index. From a28da767b5ad06c082d8af3ec23a9609834d9a94 Mon Sep 17 00:00:00 2001 From: Diego K <43053772+diegokingston@users.noreply.github.com> Date: Tue, 1 Oct 2024 16:52:50 -0300 Subject: [PATCH 22/22] Update merkle.rs --- crypto/src/merkle_tree/merkle.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index a3599e882..103d3d147 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -55,10 +55,10 @@ where // #[cfg(test)] // print_positions(nodes.len(), HashSet::new()); - MerkleTree { + Some(MerkleTree { root: nodes[ROOT].clone(), nodes, - } + }) } /// Returns the leaf at the given index.