From e02507d11ee6c184643fe70d0cc7b10a1ed81347 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sat, 8 Apr 2023 12:46:53 -0700 Subject: [PATCH 01/11] chore: update version to v0.4.0 --- CHANGELOG.md | 3 +++ Cargo.toml | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 461680b..79ca146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.4.0 (TBD) + + ## 0.3.0 (2023-04-08) - Added `depth` parameter to SMT constructors in `MerkleStore` (#115). diff --git a/Cargo.toml b/Cargo.toml index 13fc97f..9b1555f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "miden-crypto" -version = "0.3.0" +version = "0.4.0" description = "Miden Cryptographic primitives" authors = ["miden contributors"] readme = "README.md" license = "MIT" repository = "https://github.com/0xPolygonMiden/crypto" -documentation = "https://docs.rs/miden-crypto/0.3.0" +documentation = "https://docs.rs/miden-crypto/0.4.0" categories = ["cryptography", "no-std"] keywords = ["miden", "crypto", "hash", "merkle"] edition = "2021" From bbb1e641a30d9bd80d2a7d8c266c3071b0129fde Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Tue, 11 Apr 2023 17:38:39 +0200 Subject: [PATCH 02/11] config: add rustfmt config --- benches/hash.rs | 8 +- benches/smt.rs | 7 +- benches/store.rs | 50 +++++-------- rustfmt.toml | 20 +++++ src/hash/blake/mod.rs | 5 +- src/hash/rpo/digest.rs | 11 ++- src/merkle/index.rs | 5 +- src/merkle/merkle_tree.rs | 32 ++------ src/merkle/mmr/accumulator.rs | 4 +- src/merkle/mmr/full.rs | 5 +- src/merkle/mmr/tests.rs | 96 +++++------------------- src/merkle/path_set.rs | 19 ++--- src/merkle/simple_smt/mod.rs | 13 +--- src/merkle/simple_smt/tests.rs | 33 ++------ src/merkle/store/mod.rs | 26 ++----- src/merkle/store/tests.rs | 133 +++++++-------------------------- 16 files changed, 126 insertions(+), 341 deletions(-) create mode 100644 rustfmt.toml diff --git a/benches/hash.rs b/benches/hash.rs index ef0680d..271c1f5 100644 --- a/benches/hash.rs +++ b/benches/hash.rs @@ -106,11 +106,5 @@ fn blake3_sequential(c: &mut Criterion) { }); } -criterion_group!( - hash_group, - rpo256_2to1, - rpo256_sequential, - blake3_2to1, - blake3_sequential -); +criterion_group!(hash_group, rpo256_2to1, rpo256_sequential, blake3_2to1, blake3_sequential); criterion_main!(hash_group); diff --git a/benches/smt.rs b/benches/smt.rs index a19c4d2..3c63c04 100644 --- a/benches/smt.rs +++ b/benches/smt.rs @@ -75,10 +75,5 @@ criterion_main!(smt_group); fn generate_word(seed: &mut [u8; 32]) -> Word { swap(seed, &mut prng_array(*seed)); let nums: [u64; 4] = prng_array(*seed); - [ - Felt::new(nums[0]), - Felt::new(nums[1]), - Felt::new(nums[2]), - Felt::new(nums[3]), - ] + [Felt::new(nums[0]), Felt::new(nums[1]), Felt::new(nums[2]), Felt::new(nums[3])] } diff --git a/benches/store.rs b/benches/store.rs index 804faa6..83ac348 100644 --- a/benches/store.rs +++ b/benches/store.rs @@ -347,16 +347,13 @@ fn new(c: &mut Criterion) { // This could be done with `bench_with_input`, however to remove variables while comparing // with MerkleTree it is using `iter_batched` - group.bench_function( - BenchmarkId::new("MerkleStore::with_merkle_tree", size), - |b| { - b.iter_batched( - || leaves.iter().map(|v| v.into()).collect::>(), - |l| black_box(MerkleStore::new().with_merkle_tree(l)), - BatchSize::SmallInput, - ) - }, - ); + group.bench_function(BenchmarkId::new("MerkleStore::with_merkle_tree", size), |b| { + b.iter_batched( + || leaves.iter().map(|v| v.into()).collect::>(), + |l| black_box(MerkleStore::new().with_merkle_tree(l)), + BatchSize::SmallInput, + ) + }); group.bench_function(BenchmarkId::new("SimpleSmt::new", size), |b| { b.iter_batched( @@ -372,26 +369,19 @@ fn new(c: &mut Criterion) { ) }); - group.bench_function( - BenchmarkId::new("MerkleStore::with_sparse_merkle_tree", size), - |b| { - b.iter_batched( - || { - leaves - .iter() - .enumerate() - .map(|(c, v)| (c.try_into().unwrap(), v.into())) - .collect::>() - }, - |l| { - black_box( - MerkleStore::new().with_sparse_merkle_tree(SimpleSmt::MAX_DEPTH, l), - ) - }, - BatchSize::SmallInput, - ) - }, - ); + group.bench_function(BenchmarkId::new("MerkleStore::with_sparse_merkle_tree", size), |b| { + b.iter_batched( + || { + leaves + .iter() + .enumerate() + .map(|(c, v)| (c.try_into().unwrap(), v.into())) + .collect::>() + }, + |l| black_box(MerkleStore::new().with_sparse_merkle_tree(SimpleSmt::MAX_DEPTH, l)), + BatchSize::SmallInput, + ) + }); } } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..93e66a1 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,20 @@ +edition = "2021" +array_width = 80 +attr_fn_like_width = 80 +chain_width = 80 +#condense_wildcard_suffixes = true +#enum_discrim_align_threshold = 40 +fn_call_width = 80 +#fn_single_line = true +#format_code_in_doc_comments = true +#format_macro_matchers = true +#format_strings = true +#group_imports = "StdExternalCrate" +#hex_literal_case = "Lower" +#imports_granularity = "Crate" +newline_style = "Unix" +#normalize_doc_attributes = true +#reorder_impl_items = true +single_line_if_else_max_width = 60 +use_field_init_shorthand = true +use_try_shorthand = true diff --git a/src/hash/blake/mod.rs b/src/hash/blake/mod.rs index a3bcfd0..91c9bca 100644 --- a/src/hash/blake/mod.rs +++ b/src/hash/blake/mod.rs @@ -270,10 +270,7 @@ impl Blake3_160 { /// Zero-copy ref shrink to array. fn shrink_bytes(bytes: &[u8; M]) -> &[u8; N] { // compile-time assertion - assert!( - M >= N, - "N should fit in M so it can be safely transmuted into a smaller slice!" - ); + assert!(M >= N, "N should fit in M so it can be safely transmuted into a smaller slice!"); // safety: bytes len is asserted unsafe { transmute(bytes) } } diff --git a/src/hash/rpo/digest.rs b/src/hash/rpo/digest.rs index 92f2a15..2edfd27 100644 --- a/src/hash/rpo/digest.rs +++ b/src/hash/rpo/digest.rs @@ -118,14 +118,13 @@ impl Ord for RpoDigest { // finally, we use `Felt::inner` instead of `Felt::as_int` so we avoid performing a // montgomery reduction for every limb. that is safe because every inner element of the // digest is guaranteed to be in its canonical form (that is, `x in [0,p)`). - self.0 - .iter() - .map(Felt::inner) - .zip(other.0.iter().map(Felt::inner)) - .fold(Ordering::Equal, |ord, (a, b)| match ord { + self.0.iter().map(Felt::inner).zip(other.0.iter().map(Felt::inner)).fold( + Ordering::Equal, + |ord, (a, b)| match ord { Ordering::Equal => a.cmp(&b), _ => ord, - }) + }, + ) } } diff --git a/src/merkle/index.rs b/src/merkle/index.rs index b4f3bdf..b1c7389 100644 --- a/src/merkle/index.rs +++ b/src/merkle/index.rs @@ -132,10 +132,7 @@ mod tests { #[test] fn test_node_index_value_too_high() { - assert_eq!( - NodeIndex::new(0, 0).unwrap(), - NodeIndex { depth: 0, value: 0 } - ); + assert_eq!(NodeIndex::new(0, 0).unwrap(), NodeIndex { depth: 0, value: 0 }); match NodeIndex::new(0, 1) { Err(MerkleError::InvalidIndex { depth, value }) => { assert_eq!(depth, 0); diff --git a/src/merkle/merkle_tree.rs b/src/merkle/merkle_tree.rs index 3587fc6..4e39a09 100644 --- a/src/merkle/merkle_tree.rs +++ b/src/merkle/merkle_tree.rs @@ -109,10 +109,7 @@ impl MerkleTree { index.move_up(); } - debug_assert!( - index.is_root(), - "the path walk must go all the way to the root" - ); + debug_assert!(index.is_root(), "the path walk must go all the way to the root"); Ok(path.into()) } @@ -248,12 +245,7 @@ mod tests { use core::mem::size_of; use proptest::prelude::*; - const LEAVES4: [Word; 4] = [ - int_to_node(1), - int_to_node(2), - int_to_node(3), - int_to_node(4), - ]; + const LEAVES4: [Word; 4] = [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)]; const LEAVES8: [Word; 8] = [ int_to_node(1), @@ -309,22 +301,10 @@ mod tests { let (_, node2, node3) = compute_internal_nodes(); // check depth 2 - assert_eq!( - vec![LEAVES4[1], node3], - *tree.get_path(NodeIndex::make(2, 0)).unwrap() - ); - assert_eq!( - vec![LEAVES4[0], node3], - *tree.get_path(NodeIndex::make(2, 1)).unwrap() - ); - assert_eq!( - vec![LEAVES4[3], node2], - *tree.get_path(NodeIndex::make(2, 2)).unwrap() - ); - assert_eq!( - vec![LEAVES4[2], node2], - *tree.get_path(NodeIndex::make(2, 3)).unwrap() - ); + assert_eq!(vec![LEAVES4[1], node3], *tree.get_path(NodeIndex::make(2, 0)).unwrap()); + assert_eq!(vec![LEAVES4[0], node3], *tree.get_path(NodeIndex::make(2, 1)).unwrap()); + assert_eq!(vec![LEAVES4[3], node2], *tree.get_path(NodeIndex::make(2, 2)).unwrap()); + assert_eq!(vec![LEAVES4[2], node2], *tree.get_path(NodeIndex::make(2, 3)).unwrap()); // check depth 1 assert_eq!(vec![node3], *tree.get_path(NodeIndex::make(1, 0)).unwrap()); diff --git a/src/merkle/mmr/accumulator.rs b/src/merkle/mmr/accumulator.rs index 2a5d522..610999a 100644 --- a/src/merkle/mmr/accumulator.rs +++ b/src/merkle/mmr/accumulator.rs @@ -54,8 +54,6 @@ impl MmrPeaks { pub fn verify(&self, value: Word, opening: MmrProof) -> bool { let root = &self.peaks[opening.peak_index()]; - opening - .merkle_path - .verify(opening.relative_pos() as u64, value, root) + opening.merkle_path.verify(opening.relative_pos() as u64, value, root) } } diff --git a/src/merkle/mmr/full.rs b/src/merkle/mmr/full.rs index d100f57..6737f7b 100644 --- a/src/merkle/mmr/full.rs +++ b/src/merkle/mmr/full.rs @@ -280,10 +280,7 @@ impl<'a> Iterator for MmrNodes<'a> { type Item = InnerNodeInfo; fn next(&mut self) -> Option { - debug_assert!( - self.last_right.count_ones() <= 1, - "last_right tracks zero or one element" - ); + debug_assert!(self.last_right.count_ones() <= 1, "last_right tracks zero or one element"); // only parent nodes are emitted, remove the single node tree from the forest let target = self.mmr.forest & (usize::MAX << 1); diff --git a/src/merkle/mmr/tests.rs b/src/merkle/mmr/tests.rs index 218a6dd..aca4ff7 100644 --- a/src/merkle/mmr/tests.rs +++ b/src/merkle/mmr/tests.rs @@ -118,9 +118,7 @@ fn test_mmr_simple() { postorder.push(LEAVES[2]); postorder.push(LEAVES[3]); postorder.push(*Rpo256::hash_elements(&[LEAVES[2], LEAVES[3]].concat())); - postorder.push(*Rpo256::hash_elements( - &[postorder[2], postorder[5]].concat(), - )); + postorder.push(*Rpo256::hash_elements(&[postorder[2], postorder[5]].concat())); postorder.push(LEAVES[4]); postorder.push(LEAVES[5]); postorder.push(*Rpo256::hash_elements(&[LEAVES[4], LEAVES[5]].concat())); @@ -201,10 +199,7 @@ fn test_mmr_open() { let h23: Word = Rpo256::hash_elements(&LEAVES[2..4].concat()).into(); // node at pos 7 is the root - assert!( - mmr.open(7).is_err(), - "Element 7 is not in the tree, result should be None" - ); + assert!(mmr.open(7).is_err(), "Element 7 is not in the tree, result should be None"); // node at pos 6 is the root let empty: MerklePath = MerklePath::new(vec![]); @@ -297,41 +292,13 @@ fn test_mmr_open() { #[test] fn test_mmr_get() { let mmr: Mmr = LEAVES.into(); - assert_eq!( - mmr.get(0).unwrap(), - LEAVES[0], - "value at pos 0 must correspond" - ); - assert_eq!( - mmr.get(1).unwrap(), - LEAVES[1], - "value at pos 1 must correspond" - ); - assert_eq!( - mmr.get(2).unwrap(), - LEAVES[2], - "value at pos 2 must correspond" - ); - assert_eq!( - mmr.get(3).unwrap(), - LEAVES[3], - "value at pos 3 must correspond" - ); - assert_eq!( - mmr.get(4).unwrap(), - LEAVES[4], - "value at pos 4 must correspond" - ); - assert_eq!( - mmr.get(5).unwrap(), - LEAVES[5], - "value at pos 5 must correspond" - ); - assert_eq!( - mmr.get(6).unwrap(), - LEAVES[6], - "value at pos 6 must correspond" - ); + assert_eq!(mmr.get(0).unwrap(), LEAVES[0], "value at pos 0 must correspond"); + assert_eq!(mmr.get(1).unwrap(), LEAVES[1], "value at pos 1 must correspond"); + assert_eq!(mmr.get(2).unwrap(), LEAVES[2], "value at pos 2 must correspond"); + assert_eq!(mmr.get(3).unwrap(), LEAVES[3], "value at pos 3 must correspond"); + assert_eq!(mmr.get(4).unwrap(), LEAVES[4], "value at pos 4 must correspond"); + assert_eq!(mmr.get(5).unwrap(), LEAVES[5], "value at pos 5 must correspond"); + assert_eq!(mmr.get(6).unwrap(), LEAVES[6], "value at pos 6 must correspond"); assert!(mmr.get(7).is_err()); } @@ -341,11 +308,7 @@ fn test_mmr_invariants() { for v in 1..=1028 { mmr.add(int_to_node(v)); let accumulator = mmr.accumulator(); - assert_eq!( - v as usize, - mmr.forest(), - "MMR leaf count must increase by one on every add" - ); + assert_eq!(v as usize, mmr.forest(), "MMR leaf count must increase by one on every add"); assert_eq!( v as usize, accumulator.num_leaves, "MMR and its accumulator must match leaves count" @@ -374,41 +337,21 @@ fn test_bit_position_iterator() { assert_eq!(TrueBitPositionIterator::new(0).count(), 0); assert_eq!(TrueBitPositionIterator::new(0).rev().count(), 0); - assert_eq!( - TrueBitPositionIterator::new(1).collect::>(), - vec![0] - ); - assert_eq!( - TrueBitPositionIterator::new(1).rev().collect::>(), - vec![0], - ); + assert_eq!(TrueBitPositionIterator::new(1).collect::>(), vec![0]); + assert_eq!(TrueBitPositionIterator::new(1).rev().collect::>(), vec![0],); - assert_eq!( - TrueBitPositionIterator::new(2).collect::>(), - vec![1] - ); - assert_eq!( - TrueBitPositionIterator::new(2).rev().collect::>(), - vec![1], - ); + assert_eq!(TrueBitPositionIterator::new(2).collect::>(), vec![1]); + assert_eq!(TrueBitPositionIterator::new(2).rev().collect::>(), vec![1],); - assert_eq!( - TrueBitPositionIterator::new(3).collect::>(), - vec![0, 1], - ); - assert_eq!( - TrueBitPositionIterator::new(3).rev().collect::>(), - vec![1, 0], - ); + assert_eq!(TrueBitPositionIterator::new(3).collect::>(), vec![0, 1],); + assert_eq!(TrueBitPositionIterator::new(3).rev().collect::>(), vec![1, 0],); assert_eq!( TrueBitPositionIterator::new(0b11010101).collect::>(), vec![0, 2, 4, 6, 7], ); assert_eq!( - TrueBitPositionIterator::new(0b11010101) - .rev() - .collect::>(), + TrueBitPositionIterator::new(0b11010101).rev().collect::>(), vec![7, 6, 4, 2, 0], ); } @@ -463,10 +406,7 @@ fn test_mmr_hash_peaks() { // minimum length is 16 let mut expected_peaks = [first_peak, second_peak, third_peak].to_vec(); expected_peaks.resize(16, [ZERO; WORD_SIZE]); - assert_eq!( - peaks.hash_peaks(), - *Rpo256::hash_elements(&expected_peaks.as_slice().concat()) - ); + assert_eq!(peaks.hash_peaks(), *Rpo256::hash_elements(&expected_peaks.as_slice().concat())); } #[test] diff --git a/src/merkle/path_set.rs b/src/merkle/path_set.rs index ef3cc48..653fab1 100644 --- a/src/merkle/path_set.rs +++ b/src/merkle/path_set.rs @@ -34,12 +34,10 @@ impl MerklePathSet { where I: IntoIterator, { - paths - .into_iter() - .try_fold(self, |mut set, (index, value, path)| { - set.add_path(index, value, path)?; - Ok(set) - }) + paths.into_iter().try_fold(self, |mut set, (index, value, path)| { + set.add_path(index, value, path)?; + Ok(set) + }) } // PUBLIC ACCESSORS @@ -291,14 +289,9 @@ mod tests { let hash_6 = int_to_node(6); let index = 6_u64; let depth = 3_u8; - let set = MerklePathSet::new(depth) - .with_paths([(index, hash_6, path_6.into())]) - .unwrap(); + let set = MerklePathSet::new(depth).with_paths([(index, hash_6, path_6.into())]).unwrap(); - assert_eq!( - int_to_node(6u64), - set.get_node(NodeIndex::make(depth, index)).unwrap() - ); + assert_eq!(int_to_node(6u64), set.get_node(NodeIndex::make(depth, index)).unwrap()); } #[test] diff --git a/src/merkle/simple_smt/mod.rs b/src/merkle/simple_smt/mod.rs index b9eda8a..1f29fe0 100644 --- a/src/merkle/simple_smt/mod.rs +++ b/src/merkle/simple_smt/mod.rs @@ -131,12 +131,7 @@ impl SimpleSmt { Err(MerkleError::DepthTooBig(index.depth() as u64)) } else if index.depth() == self.depth() { self.get_leaf_node(index.value()) - .or_else(|| { - self.empty_hashes - .get(index.depth() as usize) - .copied() - .map(Word::from) - }) + .or_else(|| self.empty_hashes.get(index.depth() as usize).copied().map(Word::from)) .ok_or(MerkleError::NodeNotInSet(index.value())) } else { let branch_node = self.get_branch_node(&index); @@ -217,11 +212,7 @@ impl SimpleSmt { let is_right = index.is_value_odd(); index.move_up(); let BranchNode { left, right } = self.get_branch_node(&index); - let (left, right) = if is_right { - (left, value) - } else { - (value, right) - }; + let (left, right) = if is_right { (left, value) } else { (value, right) }; self.insert_branch_node(index, left, right); value = Rpo256::merge(&[left, right]); } diff --git a/src/merkle/simple_smt/tests.rs b/src/merkle/simple_smt/tests.rs index 82938d3..582e003 100644 --- a/src/merkle/simple_smt/tests.rs +++ b/src/merkle/simple_smt/tests.rs @@ -8,12 +8,7 @@ use rand_utils::prng_array; const KEYS4: [u64; 4] = [0, 1, 2, 3]; const KEYS8: [u64; 8] = [0, 1, 2, 3, 4, 5, 6, 7]; -const VALUES4: [Word; 4] = [ - int_to_node(1), - int_to_node(2), - int_to_node(3), - int_to_node(4), -]; +const VALUES4: [Word; 4] = [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)]; const VALUES8: [Word; 8] = [ int_to_node(1), @@ -56,8 +51,7 @@ fn build_sparse_tree() { let key = 6; let new_node = int_to_node(7); values[key as usize] = new_node; - smt.insert_leaf(key, new_node) - .expect("Failed to insert leaf"); + smt.insert_leaf(key, new_node).expect("Failed to insert leaf"); let mt2 = MerkleTree::new(values.clone()).unwrap(); assert_eq!(mt2.root(), smt.root()); assert_eq!( @@ -69,8 +63,7 @@ fn build_sparse_tree() { let key = 2; let new_node = int_to_node(3); values[key as usize] = new_node; - smt.insert_leaf(key, new_node) - .expect("Failed to insert leaf"); + smt.insert_leaf(key, new_node).expect("Failed to insert leaf"); let mt3 = MerkleTree::new(values).unwrap(); assert_eq!(mt3.root(), smt.root()); assert_eq!( @@ -116,22 +109,10 @@ fn get_path() { let (_, node2, node3) = compute_internal_nodes(); // check depth 2 - assert_eq!( - vec![VALUES4[1], node3], - *tree.get_path(NodeIndex::make(2, 0)).unwrap() - ); - assert_eq!( - vec![VALUES4[0], node3], - *tree.get_path(NodeIndex::make(2, 1)).unwrap() - ); - assert_eq!( - vec![VALUES4[3], node2], - *tree.get_path(NodeIndex::make(2, 2)).unwrap() - ); - assert_eq!( - vec![VALUES4[2], node2], - *tree.get_path(NodeIndex::make(2, 3)).unwrap() - ); + assert_eq!(vec![VALUES4[1], node3], *tree.get_path(NodeIndex::make(2, 0)).unwrap()); + assert_eq!(vec![VALUES4[0], node3], *tree.get_path(NodeIndex::make(2, 1)).unwrap()); + assert_eq!(vec![VALUES4[3], node2], *tree.get_path(NodeIndex::make(2, 2)).unwrap()); + assert_eq!(vec![VALUES4[2], node2], *tree.get_path(NodeIndex::make(2, 3)).unwrap()); // check depth 1 assert_eq!(vec![node3], *tree.get_path(NodeIndex::make(1, 0)).unwrap()); diff --git a/src/merkle/store/mod.rs b/src/merkle/store/mod.rs index a938c4f..7d11f2d 100644 --- a/src/merkle/store/mod.rs +++ b/src/merkle/store/mod.rs @@ -184,15 +184,11 @@ impl MerkleStore { let mut hash: RpoDigest = root.into(); // corner case: check the root is in the store when called with index `NodeIndex::root()` - self.nodes - .get(&hash) - .ok_or(MerkleError::RootNotInStore(hash.into()))?; + self.nodes.get(&hash).ok_or(MerkleError::RootNotInStore(hash.into()))?; for i in (0..index.depth()).rev() { - let node = self - .nodes - .get(&hash) - .ok_or(MerkleError::NodeNotInStore(hash.into(), index))?; + let node = + self.nodes.get(&hash).ok_or(MerkleError::NodeNotInStore(hash.into(), index))?; let bit = (index.value() >> i) & 1; hash = if bit == 0 { node.left } else { node.right } @@ -215,15 +211,11 @@ impl MerkleStore { let mut path = Vec::with_capacity(index.depth().into()); // corner case: check the root is in the store when called with index `NodeIndex::root()` - self.nodes - .get(&hash) - .ok_or(MerkleError::RootNotInStore(hash.into()))?; + self.nodes.get(&hash).ok_or(MerkleError::RootNotInStore(hash.into()))?; for i in (0..index.depth()).rev() { - let node = self - .nodes - .get(&hash) - .ok_or(MerkleError::NodeNotInStore(hash.into(), index))?; + let node = + self.nodes.get(&hash).ok_or(MerkleError::NodeNotInStore(hash.into(), index))?; let bit = (index.value() >> i) & 1; hash = if bit == 0 { @@ -302,11 +294,7 @@ impl MerkleStore { }; // traverse down - hash = if path & 1 == 0 { - children.left - } else { - children.right - }; + hash = if path & 1 == 0 { children.left } else { children.right }; path >>= 1; } diff --git a/src/merkle/store/tests.rs b/src/merkle/store/tests.rs index de65b1f..251cd9a 100644 --- a/src/merkle/store/tests.rs +++ b/src/merkle/store/tests.rs @@ -9,12 +9,7 @@ use crate::{ use std::error::Error; const KEYS4: [u64; 4] = [0, 1, 2, 3]; -const LEAVES4: [Word; 4] = [ - int_to_node(1), - int_to_node(2), - int_to_node(3), - int_to_node(4), -]; +const LEAVES4: [Word; 4] = [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)]; const EMPTY: Word = [ZERO; WORD_SIZE]; #[test] @@ -90,9 +85,7 @@ fn test_merkle_tree() -> Result<(), MerkleError> { // STORE MERKLE PATH MATCHS ============================================================== // assert the merkle path returned by the store is the same as the one in the tree - let result = store - .get_path(mtree.root(), NodeIndex::make(mtree.depth(), 0)) - .unwrap(); + let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 0)).unwrap(); assert_eq!( LEAVES4[0], result.value, "Value for merkle path at index 0 must match leaf value" @@ -103,9 +96,7 @@ fn test_merkle_tree() -> Result<(), MerkleError> { "merkle path for index 0 must be the same for the MerkleTree and MerkleStore" ); - let result = store - .get_path(mtree.root(), NodeIndex::make(mtree.depth(), 1)) - .unwrap(); + let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 1)).unwrap(); assert_eq!( LEAVES4[1], result.value, "Value for merkle path at index 0 must match leaf value" @@ -116,9 +107,7 @@ fn test_merkle_tree() -> Result<(), MerkleError> { "merkle path for index 1 must be the same for the MerkleTree and MerkleStore" ); - let result = store - .get_path(mtree.root(), NodeIndex::make(mtree.depth(), 2)) - .unwrap(); + let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 2)).unwrap(); assert_eq!( LEAVES4[2], result.value, "Value for merkle path at index 0 must match leaf value" @@ -129,9 +118,7 @@ fn test_merkle_tree() -> Result<(), MerkleError> { "merkle path for index 0 must be the same for the MerkleTree and MerkleStore" ); - let result = store - .get_path(mtree.root(), NodeIndex::make(mtree.depth(), 3)) - .unwrap(); + let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 3)).unwrap(); assert_eq!( LEAVES4[3], result.value, "Value for merkle path at index 0 must match leaf value" @@ -172,10 +159,7 @@ fn test_leaf_paths_for_empty_trees() -> Result<(), MerkleError> { let index = NodeIndex::make(depth, 0); let store_path = store.get_path(smt.root(), index)?; let smt_path = smt.get_path(index)?; - assert_eq!( - store_path.value, EMPTY, - "the leaf of an empty tree is always ZERO" - ); + assert_eq!(store_path.value, EMPTY, "the leaf of an empty tree is always ZERO"); assert_eq!( store_path.path, smt_path, "the returned merkle path does not match the computed values" @@ -213,17 +197,11 @@ fn test_add_sparse_merkle_tree_one_level() -> Result<(), MerkleError> { let idx = NodeIndex::make(1, 0); assert_eq!(smt.get_node(idx).unwrap(), leaves2[0]); - assert_eq!( - store.get_node(smt.root(), idx).unwrap(), - smt.get_node(idx).unwrap() - ); + assert_eq!(store.get_node(smt.root(), idx).unwrap(), smt.get_node(idx).unwrap()); let idx = NodeIndex::make(1, 1); assert_eq!(smt.get_node(idx).unwrap(), leaves2[1]); - assert_eq!( - store.get_node(smt.root(), idx).unwrap(), - smt.get_node(idx).unwrap() - ); + assert_eq!(store.get_node(smt.root(), idx).unwrap(), smt.get_node(idx).unwrap()); Ok(()) } @@ -231,10 +209,8 @@ fn test_add_sparse_merkle_tree_one_level() -> Result<(), MerkleError> { #[test] fn test_sparse_merkle_tree() -> Result<(), MerkleError> { let mut store = MerkleStore::default(); - store.add_sparse_merkle_tree( - SimpleSmt::MAX_DEPTH, - KEYS4.into_iter().zip(LEAVES4.into_iter()), - )?; + store + .add_sparse_merkle_tree(SimpleSmt::MAX_DEPTH, KEYS4.into_iter().zip(LEAVES4.into_iter()))?; let smt = SimpleSmt::new(SimpleSmt::MAX_DEPTH) .unwrap() @@ -299,9 +275,7 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> { // STORE MERKLE PATH MATCHS ============================================================== // assert the merkle path returned by the store is the same as the one in the tree - let result = store - .get_path(smt.root(), NodeIndex::make(smt.depth(), 0)) - .unwrap(); + let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 0)).unwrap(); assert_eq!( LEAVES4[0], result.value, "Value for merkle path at index 0 must match leaf value" @@ -312,9 +286,7 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> { "merkle path for index 0 must be the same for the MerkleTree and MerkleStore" ); - let result = store - .get_path(smt.root(), NodeIndex::make(smt.depth(), 1)) - .unwrap(); + let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 1)).unwrap(); assert_eq!( LEAVES4[1], result.value, "Value for merkle path at index 1 must match leaf value" @@ -325,9 +297,7 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> { "merkle path for index 1 must be the same for the MerkleTree and MerkleStore" ); - let result = store - .get_path(smt.root(), NodeIndex::make(smt.depth(), 2)) - .unwrap(); + let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 2)).unwrap(); assert_eq!( LEAVES4[2], result.value, "Value for merkle path at index 2 must match leaf value" @@ -338,9 +308,7 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> { "merkle path for index 2 must be the same for the MerkleTree and MerkleStore" ); - let result = store - .get_path(smt.root(), NodeIndex::make(smt.depth(), 3)) - .unwrap(); + let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 3)).unwrap(); assert_eq!( LEAVES4[3], result.value, "Value for merkle path at index 3 must match leaf value" @@ -351,13 +319,8 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> { "merkle path for index 3 must be the same for the MerkleTree and MerkleStore" ); - let result = store - .get_path(smt.root(), NodeIndex::make(smt.depth(), 4)) - .unwrap(); - assert_eq!( - EMPTY, result.value, - "Value for merkle path at index 4 must match leaf value" - ); + let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 4)).unwrap(); + assert_eq!(EMPTY, result.value, "Value for merkle path at index 4 must match leaf value"); assert_eq!( smt.get_path(NodeIndex::make(smt.depth(), 4)), Ok(result.path), @@ -391,9 +354,7 @@ fn test_add_merkle_paths() -> Result<(), MerkleError> { ]; let mut store = MerkleStore::default(); - store - .add_merkle_paths(paths.clone()) - .expect("the valid paths must work"); + store.add_merkle_paths(paths.clone()).expect("the valid paths must work"); let depth = 2; let set = MerklePathSet::new(depth).with_paths(paths).unwrap(); @@ -446,9 +407,7 @@ fn test_add_merkle_paths() -> Result<(), MerkleError> { // STORE MERKLE PATH MATCHS ============================================================== // assert the merkle path returned by the store is the same as the one in the set - let result = store - .get_path(set.root(), NodeIndex::make(set.depth(), 0)) - .unwrap(); + let result = store.get_path(set.root(), NodeIndex::make(set.depth(), 0)).unwrap(); assert_eq!( LEAVES4[0], result.value, "Value for merkle path at index 0 must match leaf value" @@ -459,9 +418,7 @@ fn test_add_merkle_paths() -> Result<(), MerkleError> { "merkle path for index 0 must be the same for the MerkleTree and MerkleStore" ); - let result = store - .get_path(set.root(), NodeIndex::make(set.depth(), 1)) - .unwrap(); + let result = store.get_path(set.root(), NodeIndex::make(set.depth(), 1)).unwrap(); assert_eq!( LEAVES4[1], result.value, "Value for merkle path at index 0 must match leaf value" @@ -472,9 +429,7 @@ fn test_add_merkle_paths() -> Result<(), MerkleError> { "merkle path for index 1 must be the same for the MerkleTree and MerkleStore" ); - let result = store - .get_path(set.root(), NodeIndex::make(set.depth(), 2)) - .unwrap(); + let result = store.get_path(set.root(), NodeIndex::make(set.depth(), 2)).unwrap(); assert_eq!( LEAVES4[2], result.value, "Value for merkle path at index 0 must match leaf value" @@ -485,9 +440,7 @@ fn test_add_merkle_paths() -> Result<(), MerkleError> { "merkle path for index 0 must be the same for the MerkleTree and MerkleStore" ); - let result = store - .get_path(set.root(), NodeIndex::make(set.depth(), 3)) - .unwrap(); + let result = store.get_path(set.root(), NodeIndex::make(set.depth(), 3)).unwrap(); assert_eq!( LEAVES4[3], result.value, "Value for merkle path at index 0 must match leaf value" @@ -546,13 +499,8 @@ fn store_path_opens_from_leaf() { let root = Rpo256::merge(&[m.into(), n.into()]); - let store = MerkleStore::default() - .with_merkle_tree([a, b, c, d, e, f, g, h]) - .unwrap(); - let path = store - .get_path(root.into(), NodeIndex::make(3, 1)) - .unwrap() - .path; + let store = MerkleStore::default().with_merkle_tree([a, b, c, d, e, f, g, h]).unwrap(); + let path = store.get_path(root.into(), NodeIndex::make(3, 1)).unwrap().path; let expected = MerklePath::new([a.into(), j.into(), n.into()].to_vec()); assert_eq!(path, expected); @@ -565,11 +513,7 @@ fn test_set_node() -> Result<(), MerkleError> { let value = int_to_node(42); let index = NodeIndex::make(mtree.depth(), 0); let new_root = store.set_node(mtree.root(), index, value)?.root; - assert_eq!( - store.get_node(new_root, index), - Ok(value), - "Value must have changed" - ); + assert_eq!(store.get_node(new_root, index), Ok(value), "Value must have changed"); Ok(()) } @@ -604,26 +548,10 @@ fn test_constructors() -> Result<(), MerkleError> { let d = 2; let paths = [ - ( - 0, - LEAVES4[0], - mtree.get_path(NodeIndex::make(d, 0)).unwrap(), - ), - ( - 1, - LEAVES4[1], - mtree.get_path(NodeIndex::make(d, 1)).unwrap(), - ), - ( - 2, - LEAVES4[2], - mtree.get_path(NodeIndex::make(d, 2)).unwrap(), - ), - ( - 3, - LEAVES4[3], - mtree.get_path(NodeIndex::make(d, 3)).unwrap(), - ), + (0, LEAVES4[0], mtree.get_path(NodeIndex::make(d, 0)).unwrap()), + (1, LEAVES4[1], mtree.get_path(NodeIndex::make(d, 1)).unwrap()), + (2, LEAVES4[2], mtree.get_path(NodeIndex::make(d, 2)).unwrap()), + (3, LEAVES4[3], mtree.get_path(NodeIndex::make(d, 3)).unwrap()), ]; let store1 = MerkleStore::default().with_merkle_paths(paths.clone())?; @@ -792,10 +720,7 @@ fn get_leaf_depth_works_with_depth_8() { // duplicate the tree on `a` and assert the depth is short-circuited by such sub-tree let index = NodeIndex::new(8, a).unwrap(); root = store.set_node(root, index, root).unwrap().root; - assert_eq!( - Err(MerkleError::DepthTooBig(9)), - store.get_leaf_depth(root, 8, a) - ); + assert_eq!(Err(MerkleError::DepthTooBig(9)), store.get_leaf_depth(root, 8, a)); } #[cfg(std)] From 79915cc346c7d37ba1045327a29b7e6dccbf606a Mon Sep 17 00:00:00 2001 From: frisitano Date: Fri, 14 Apr 2023 13:25:19 +0100 Subject: [PATCH 03/11] feat: re-export MmrProof --- src/merkle/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/merkle/mod.rs b/src/merkle/mod.rs index 4d36577..aca3dde 100644 --- a/src/merkle/mod.rs +++ b/src/merkle/mod.rs @@ -27,7 +27,7 @@ mod simple_smt; pub use simple_smt::SimpleSmt; mod mmr; -pub use mmr::{Mmr, MmrPeaks}; +pub use mmr::{Mmr, MmrPeaks, MmrProof}; mod store; pub use store::MerkleStore; From 3996374a8bb647053af056563a38ad148be5183d Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Fri, 14 Apr 2023 20:35:08 +0200 Subject: [PATCH 04/11] feat: allow merging of leaves Consider the case of a MMR with one entry, and a new entry is being added. Both of these values are quite unique, they are at the same time the root and only leaf of their corresponding tree. Currently this representation is not supported by the [MerkleStore], so the leaves are not in it. Once the two values are merged, they both become leaves of a new tree under the new parent, and the existing validation didn't permit that promotion from happening. This lifts the validation, and changes the method to clarify that not only `root` are being merged, by arbitrary nodes of a tree (leafs, internal, or roots), with arbitrary mixing of each. --- src/merkle/store/mod.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/merkle/store/mod.rs b/src/merkle/store/mod.rs index 7d11f2d..a510826 100644 --- a/src/merkle/store/mod.rs +++ b/src/merkle/store/mod.rs @@ -478,26 +478,23 @@ impl MerkleStore { Ok(RootPath { root, path }) } + /// Merges two elements and adds the resulting node into the store. + /// + /// Merges arbitrary values. They may be leafs, nodes, or a mixture of both. pub fn merge_roots(&mut self, root1: Word, root2: Word) -> Result { let root1: RpoDigest = root1.into(); let root2: RpoDigest = root2.into(); - if !self.nodes.contains_key(&root1) { - Err(MerkleError::NodeNotInStore(root1.into(), NodeIndex::root())) - } else if !self.nodes.contains_key(&root1) { - Err(MerkleError::NodeNotInStore(root2.into(), NodeIndex::root())) - } else { - let parent: Word = Rpo256::merge(&[root1, root2]).into(); - self.nodes.insert( - parent.into(), - Node { - left: root1, - right: root2, - }, - ); + let parent: Word = Rpo256::merge(&[root1, root2]).into(); + self.nodes.insert( + parent.into(), + Node { + left: root1, + right: root2, + }, + ); - Ok(parent) - } + Ok(parent) } } From 8161477d6a03d8dcd510f49be030d9291fa0b7db Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Wed, 19 Apr 2023 15:30:16 +0200 Subject: [PATCH 05/11] store: support adding existing structures --- src/merkle/store/mod.rs | 77 +++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/src/merkle/store/mod.rs b/src/merkle/store/mod.rs index a510826..5182bd2 100644 --- a/src/merkle/store/mod.rs +++ b/src/merkle/store/mod.rs @@ -1,7 +1,7 @@ use super::mmr::{Mmr, MmrPeaks}; use super::{ - BTreeMap, EmptySubtreeRoots, MerkleError, MerklePath, MerklePathSet, MerkleTree, NodeIndex, - RootPath, Rpo256, RpoDigest, SimpleSmt, ValuePath, Vec, Word, + BTreeMap, EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, MerklePathSet, MerkleTree, + NodeIndex, RootPath, Rpo256, RpoDigest, SimpleSmt, ValuePath, Vec, Word, }; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -310,6 +310,20 @@ impl MerkleStore { // STATE MUTATORS // -------------------------------------------------------------------------------------------- + /// Adds a new [InnerNodeInfo] into the store. + pub fn extend(&mut self, iter: impl Iterator) -> &mut MerkleStore { + for node in iter { + let value: RpoDigest = node.value.into(); + let left: RpoDigest = node.left.into(); + let right: RpoDigest = node.right.into(); + + debug_assert_eq!(Rpo256::merge(&[left, right]), value); + self.nodes.insert(value, Node { left, right }); + } + + self + } + /// Adds all the nodes of a Merkle tree represented by `leaves`. /// /// This will instantiate a Merkle tree using `leaves` and include all the nodes into the @@ -330,15 +344,7 @@ impl MerkleStore { } let tree = MerkleTree::new(leaves)?; - for node in tree.inner_nodes() { - self.nodes.insert( - node.value.into(), - Node { - left: node.left.into(), - right: node.right.into(), - }, - ); - } + self.extend(tree.inner_nodes()); Ok(tree.root()) } @@ -361,15 +367,7 @@ impl MerkleStore { I: Iterator + ExactSizeIterator, { let smt = SimpleSmt::new(depth)?.with_leaves(entries)?; - for node in smt.inner_nodes() { - self.nodes.insert( - node.value.into(), - Node { - left: node.left.into(), - right: node.right.into(), - }, - ); - } + self.extend(smt.inner_nodes()); Ok(smt.root()) } @@ -441,15 +439,7 @@ impl MerkleStore { I: IntoIterator, { let mmr = Mmr::from(leaves); - for node in mmr.inner_nodes() { - self.nodes.insert( - node.value.into(), - Node { - left: node.left.into(), - right: node.right.into(), - }, - ); - } + self.extend(mmr.inner_nodes()); Ok(mmr.accumulator()) } @@ -482,19 +472,22 @@ impl MerkleStore { /// /// Merges arbitrary values. They may be leafs, nodes, or a mixture of both. pub fn merge_roots(&mut self, root1: Word, root2: Word) -> Result { - let root1: RpoDigest = root1.into(); - let root2: RpoDigest = root2.into(); - - let parent: Word = Rpo256::merge(&[root1, root2]).into(); - self.nodes.insert( - parent.into(), - Node { - left: root1, - right: root2, - }, - ); - - Ok(parent) + let left: RpoDigest = root1.into(); + let right: RpoDigest = root2.into(); + + let parent = Rpo256::merge(&[left, right]); + self.nodes.insert(parent, Node { left, right }); + + Ok(parent.into()) + } +} + +// ITERATORS +// ================================================================================================ + +impl Extend for MerkleStore { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter()); } } From eb316f51bca0486a76585c2282db8302ee49d8c0 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Thu, 20 Apr 2023 22:42:42 +0200 Subject: [PATCH 06/11] store: remove SimpleSMT/MerkleTree/Mmr add/with methods --- benches/store.rs | 46 +++++++++------- src/merkle/store/mod.rs | 107 ++++---------------------------------- src/merkle/store/tests.rs | 49 +++++++++-------- 3 files changed, 62 insertions(+), 140 deletions(-) diff --git a/benches/store.rs b/benches/store.rs index 83ac348..2ad2790 100644 --- a/benches/store.rs +++ b/benches/store.rs @@ -66,7 +66,8 @@ fn get_leaf_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let store = MerkleStore::new().with_merkle_tree(mtree_leaves).unwrap(); + let mut store = MerkleStore::new(); + store.extend(mtree.inner_nodes()); let depth = mtree.depth(); let root = mtree.root(); let size_u64 = size as u64; @@ -108,9 +109,8 @@ fn get_leaf_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let store = MerkleStore::new() - .with_sparse_merkle_tree(SimpleSmt::MAX_DEPTH, smt_leaves) - .unwrap(); + let mut store = MerkleStore::new(); + store.extend(smt.inner_nodes()); let depth = smt.depth(); let root = smt.root(); let size_u64 = size as u64; @@ -178,7 +178,8 @@ fn get_node_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let store = MerkleStore::new().with_merkle_tree(mtree_leaves).unwrap(); + let mut store = MerkleStore::new(); + store.extend(mtree.inner_nodes()); let root = mtree.root(); let half_depth = mtree.depth() / 2; let half_size = 2_u64.pow(half_depth as u32); @@ -221,9 +222,8 @@ fn get_node_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let store = MerkleStore::new() - .with_sparse_merkle_tree(SimpleSmt::MAX_DEPTH, smt_leaves) - .unwrap(); + let mut store = MerkleStore::new(); + store.extend(smt.inner_nodes()); let root = smt.root(); let half_depth = smt.depth() / 2; let half_size = 2_u64.pow(half_depth as u32); @@ -258,7 +258,8 @@ fn get_leaf_path_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let store = MerkleStore::new().with_merkle_tree(mtree_leaves).unwrap(); + let mut store = MerkleStore::new(); + store.extend(mtree.inner_nodes()); let depth = mtree.depth(); let root = mtree.root(); let size_u64 = size as u64; @@ -300,9 +301,8 @@ fn get_leaf_path_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let store = MerkleStore::new() - .with_sparse_merkle_tree(SimpleSmt::MAX_DEPTH, smt_leaves) - .unwrap(); + let mut store = MerkleStore::new(); + store.extend(smt.inner_nodes()); let depth = smt.depth(); let root = smt.root(); let size_u64 = size as u64; @@ -347,10 +347,13 @@ fn new(c: &mut Criterion) { // This could be done with `bench_with_input`, however to remove variables while comparing // with MerkleTree it is using `iter_batched` - group.bench_function(BenchmarkId::new("MerkleStore::with_merkle_tree", size), |b| { + group.bench_function(BenchmarkId::new("MerkleStore::extend::MerkleTree", size), |b| { b.iter_batched( || leaves.iter().map(|v| v.into()).collect::>(), - |l| black_box(MerkleStore::new().with_merkle_tree(l)), + |l| { + let mtree = MerkleTree::new(l).unwrap(); + black_box(MerkleStore::new().extend(mtree.inner_nodes())); + }, BatchSize::SmallInput, ) }); @@ -369,7 +372,7 @@ fn new(c: &mut Criterion) { ) }); - group.bench_function(BenchmarkId::new("MerkleStore::with_sparse_merkle_tree", size), |b| { + group.bench_function(BenchmarkId::new("MerkleStore::extend::SimpleSmt", size), |b| { b.iter_batched( || { leaves @@ -378,7 +381,10 @@ fn new(c: &mut Criterion) { .map(|(c, v)| (c.try_into().unwrap(), v.into())) .collect::>() }, - |l| black_box(MerkleStore::new().with_sparse_merkle_tree(SimpleSmt::MAX_DEPTH, l)), + |l| { + let smt = SimpleSmt::new(SimpleSmt::MAX_DEPTH).unwrap().with_leaves(l).unwrap(); + black_box(MerkleStore::new().extend(smt.inner_nodes())); + }, BatchSize::SmallInput, ) }); @@ -397,7 +403,8 @@ fn update_leaf_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mut mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let mut store = MerkleStore::new().with_merkle_tree(mtree_leaves).unwrap(); + let mut store = MerkleStore::new(); + store.extend(mtree.inner_nodes()); let depth = mtree.depth(); let root = mtree.root(); let size_u64 = size as u64; @@ -446,9 +453,8 @@ fn update_leaf_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let mut store = MerkleStore::new() - .with_sparse_merkle_tree(SimpleSmt::MAX_DEPTH, smt_leaves) - .unwrap(); + let mut store = MerkleStore::new(); + store.extend(smt.inner_nodes()); let depth = smt.depth(); let root = smt.root(); let size_u64 = size as u64; diff --git a/src/merkle/store/mod.rs b/src/merkle/store/mod.rs index 5182bd2..776da47 100644 --- a/src/merkle/store/mod.rs +++ b/src/merkle/store/mod.rs @@ -1,7 +1,7 @@ -use super::mmr::{Mmr, MmrPeaks}; +use super::mmr::Mmr; use super::{ - BTreeMap, EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, MerklePathSet, MerkleTree, - NodeIndex, RootPath, Rpo256, RpoDigest, SimpleSmt, ValuePath, Vec, Word, + BTreeMap, EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, MerklePathSet, NodeIndex, + RootPath, Rpo256, RpoDigest, ValuePath, Vec, Word, }; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -47,9 +47,13 @@ pub struct Node { /// // the store is initialized with the SMT empty nodes /// assert_eq!(store.num_internal_nodes(), 255); /// +/// let tree1 = MerkleTree::new(vec![A, B, C, D, E, F, G, H0]).unwrap(); +/// let tree2 = MerkleTree::new(vec![A, B, C, D, E, F, G, H1]).unwrap(); +/// /// // populates the store with two merkle trees, common nodes are shared -/// store.add_merkle_tree([A, B, C, D, E, F, G, H0]); -/// store.add_merkle_tree([A, B, C, D, E, F, G, H1]); +/// store +/// .extend(tree1.inner_nodes()) +/// .extend(tree2.inner_nodes()); /// /// // every leaf except the last are the same /// for i in 0..7 { @@ -111,31 +115,6 @@ impl MerkleStore { MerkleStore { nodes } } - /// Appends the provided merkle tree represented by its `leaves` to the set. - pub fn with_merkle_tree(mut self, leaves: I) -> Result - where - I: IntoIterator, - { - self.add_merkle_tree(leaves)?; - Ok(self) - } - - /// Appends the provided Sparse Merkle tree represented by its `entries` to the set. - /// - /// For more information, check [MerkleStore::add_sparse_merkle_tree]. - pub fn with_sparse_merkle_tree( - mut self, - depth: u8, - entries: R, - ) -> Result - where - R: IntoIterator, - I: Iterator + ExactSizeIterator, - { - self.add_sparse_merkle_tree(depth, entries)?; - Ok(self) - } - /// Appends the provided merkle path set. pub fn with_merkle_path( mut self, @@ -156,15 +135,6 @@ impl MerkleStore { Ok(self) } - /// Appends the provided [Mmr] represented by its `leaves` to the set. - pub fn with_mmr(mut self, leaves: I) -> Result - where - I: IntoIterator, - { - self.add_mmr(leaves)?; - Ok(self) - } - // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- @@ -324,54 +294,6 @@ impl MerkleStore { self } - /// Adds all the nodes of a Merkle tree represented by `leaves`. - /// - /// This will instantiate a Merkle tree using `leaves` and include all the nodes into the - /// store. - /// - /// # Errors - /// - /// This method may return the following errors: - /// - `DepthTooSmall` if leaves is empty or contains only 1 element - /// - `NumLeavesNotPowerOfTwo` if the number of leaves is not a power-of-two - pub fn add_merkle_tree(&mut self, leaves: I) -> Result - where - I: IntoIterator, - { - let leaves: Vec<_> = leaves.into_iter().collect(); - if leaves.len() < 2 { - return Err(MerkleError::DepthTooSmall(leaves.len() as u8)); - } - - let tree = MerkleTree::new(leaves)?; - self.extend(tree.inner_nodes()); - - Ok(tree.root()) - } - - /// Adds a Sparse Merkle tree defined by the specified `entries` to the store, and returns the - /// root of the added tree. - /// - /// The entries are expected to contain tuples of `(index, node)` describing nodes in the tree - /// at `depth`. - /// - /// # Errors - /// Returns an error if the provided `depth` is greater than [SimpleSmt::MAX_DEPTH]. - pub fn add_sparse_merkle_tree( - &mut self, - depth: u8, - entries: R, - ) -> Result - where - R: IntoIterator, - I: Iterator + ExactSizeIterator, - { - let smt = SimpleSmt::new(depth)?.with_leaves(entries)?; - self.extend(smt.inner_nodes()); - - Ok(smt.root()) - } - /// Adds all the nodes of a Merkle path represented by `path`, opening to `node`. Returns the /// new root. /// @@ -433,17 +355,6 @@ impl MerkleStore { Ok(root) } - /// Appends the provided [Mmr] into the store. - pub fn add_mmr(&mut self, leaves: I) -> Result - where - I: IntoIterator, - { - let mmr = Mmr::from(leaves); - self.extend(mmr.inner_nodes()); - - Ok(mmr.accumulator()) - } - /// Sets a node to `value`. /// /// # Errors diff --git a/src/merkle/store/tests.rs b/src/merkle/store/tests.rs index 251cd9a..3df4f21 100644 --- a/src/merkle/store/tests.rs +++ b/src/merkle/store/tests.rs @@ -1,7 +1,7 @@ use super::*; use crate::{ hash::rpo::Rpo256, - merkle::{int_to_node, MerklePathSet}, + merkle::{int_to_node, MerklePathSet, MerkleTree, SimpleSmt}, Felt, Word, WORD_SIZE, ZERO, }; @@ -15,7 +15,8 @@ const EMPTY: Word = [ZERO; WORD_SIZE]; #[test] fn test_root_not_in_store() -> Result<(), MerkleError> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let store = MerkleStore::default().with_merkle_tree(LEAVES4)?; + let mut store = MerkleStore::new(); + store.extend(mtree.inner_nodes()); assert_eq!( store.get_node(LEAVES4[0], NodeIndex::make(mtree.depth(), 0)), Err(MerkleError::RootNotInStore(LEAVES4[0])), @@ -32,10 +33,9 @@ fn test_root_not_in_store() -> Result<(), MerkleError> { #[test] fn test_merkle_tree() -> Result<(), MerkleError> { - let mut store = MerkleStore::default(); - let mtree = MerkleTree::new(LEAVES4.to_vec())?; - store.add_merkle_tree(LEAVES4.to_vec())?; + let mut store = MerkleStore::default(); + store.extend(mtree.inner_nodes()); // STORE LEAVES ARE CORRECT ============================================================== // checks the leaves in the store corresponds to the expected values @@ -176,24 +176,22 @@ fn test_leaf_paths_for_empty_trees() -> Result<(), MerkleError> { #[test] fn test_get_invalid_node() { - let mut store = MerkleStore::default(); let mtree = MerkleTree::new(LEAVES4.to_vec()).expect("creating a merkle tree must work"); - store - .add_merkle_tree(LEAVES4.to_vec()) - .expect("adding a merkle tree to the store must work"); + let mut store = MerkleStore::default(); + store.extend(mtree.inner_nodes()); let _ = store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 3)); } #[test] fn test_add_sparse_merkle_tree_one_level() -> Result<(), MerkleError> { - let mut store = MerkleStore::default(); let keys2: [u64; 2] = [0, 1]; let leaves2: [Word; 2] = [int_to_node(1), int_to_node(2)]; - store.add_sparse_merkle_tree(48, keys2.into_iter().zip(leaves2.into_iter()))?; let smt = SimpleSmt::new(1) .unwrap() .with_leaves(keys2.into_iter().zip(leaves2.into_iter())) .unwrap(); + let mut store = MerkleStore::default(); + store.extend(smt.inner_nodes()); let idx = NodeIndex::make(1, 0); assert_eq!(smt.get_node(idx).unwrap(), leaves2[0]); @@ -208,15 +206,14 @@ fn test_add_sparse_merkle_tree_one_level() -> Result<(), MerkleError> { #[test] fn test_sparse_merkle_tree() -> Result<(), MerkleError> { - let mut store = MerkleStore::default(); - store - .add_sparse_merkle_tree(SimpleSmt::MAX_DEPTH, KEYS4.into_iter().zip(LEAVES4.into_iter()))?; - let smt = SimpleSmt::new(SimpleSmt::MAX_DEPTH) .unwrap() .with_leaves(KEYS4.into_iter().zip(LEAVES4.into_iter())) .unwrap(); + let mut store = MerkleStore::default(); + store.extend(smt.inner_nodes()); + // STORE LEAVES ARE CORRECT ============================================================== // checks the leaves in the store corresponds to the expected values assert_eq!( @@ -472,7 +469,9 @@ fn wont_open_to_different_depth_root() { // For this example, the depth of the Merkle tree is 1, as we have only two leaves. Here we // attempt to fetch a node on the maximum depth, and it should fail because the root shouldn't // exist for the set. - let store = MerkleStore::default().with_merkle_tree([a, b]).unwrap(); + let mtree = MerkleTree::new(vec![a, b]).unwrap(); + let mut store = MerkleStore::new(); + store.extend(mtree.inner_nodes()); let index = NodeIndex::root(); let err = store.get_node(root, index).err().unwrap(); assert_eq!(err, MerkleError::RootNotInStore(root)); @@ -499,7 +498,9 @@ fn store_path_opens_from_leaf() { let root = Rpo256::merge(&[m.into(), n.into()]); - let store = MerkleStore::default().with_merkle_tree([a, b, c, d, e, f, g, h]).unwrap(); + let mtree = MerkleTree::new(vec![a, b, c, d, e, f, g, h]).unwrap(); + let mut store = MerkleStore::new(); + store.extend(mtree.inner_nodes()); let path = store.get_path(root.into(), NodeIndex::make(3, 1)).unwrap().path; let expected = MerklePath::new([a.into(), j.into(), n.into()].to_vec()); @@ -509,7 +510,8 @@ fn store_path_opens_from_leaf() { #[test] fn test_set_node() -> Result<(), MerkleError> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let mut store = MerkleStore::default().with_merkle_tree(LEAVES4)?; + let mut store = MerkleStore::new(); + store.extend(mtree.inner_nodes()); let value = int_to_node(42); let index = NodeIndex::make(mtree.depth(), 0); let new_root = store.set_node(mtree.root(), index, value)?.root; @@ -520,8 +522,9 @@ fn test_set_node() -> Result<(), MerkleError> { #[test] fn test_constructors() -> Result<(), MerkleError> { - let store = MerkleStore::new().with_merkle_tree(LEAVES4)?; let mtree = MerkleTree::new(LEAVES4.to_vec())?; + let mut store = MerkleStore::new(); + store.extend(mtree.inner_nodes()); let depth = mtree.depth(); let leaves = 2u64.pow(depth.into()); @@ -532,12 +535,12 @@ fn test_constructors() -> Result<(), MerkleError> { } let depth = 32; - let store = MerkleStore::default() - .with_sparse_merkle_tree(depth, KEYS4.into_iter().zip(LEAVES4.into_iter()))?; let smt = SimpleSmt::new(depth) .unwrap() .with_leaves(KEYS4.into_iter().zip(LEAVES4.into_iter())) .unwrap(); + let mut store = MerkleStore::new(); + store.extend(smt.inner_nodes()); let depth = smt.depth(); for key in KEYS4 { @@ -726,7 +729,9 @@ fn get_leaf_depth_works_with_depth_8() { #[cfg(std)] #[test] fn test_serialization() -> Result<(), Box> { - let original = MerkleStore::new().with_merkle_tree(LEAVES4)?; + let mtree = MerkleTree::new(LEAVES4.to_vec())?; + let mut store = MerkleStore::new(); + store.extend(mtree.inner_nodes()); let decoded = MerkleStore::read_from_bytes(&original.to_bytes())?; assert_eq!(original, decoded); Ok(()) From 59595a2e0465ccf7de1893e986bb909176bc23d9 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Fri, 21 Apr 2023 14:45:50 +0200 Subject: [PATCH 07/11] feat: added From convertions for the MerkleStore --- benches/store.rs | 32 ++++++++++++-------------------- src/merkle/store/mod.rs | 31 +++++++++++++++++++++++++++++-- src/merkle/store/tests.rs | 33 +++++++++++---------------------- 3 files changed, 52 insertions(+), 44 deletions(-) diff --git a/benches/store.rs b/benches/store.rs index 2ad2790..2319128 100644 --- a/benches/store.rs +++ b/benches/store.rs @@ -34,7 +34,7 @@ fn get_empty_leaf_simplesmt(c: &mut Criterion) { // both SMT and the store are pre-populated with empty hashes, accessing these values is what is // being benchmarked here, so no values are inserted into the backends let smt = SimpleSmt::new(depth).unwrap(); - let store = MerkleStore::new(); + let store: MerkleStore = smt.clone().into(); let root = smt.root(); group.bench_function(BenchmarkId::new("SimpleSmt", depth), |b| { @@ -66,8 +66,7 @@ fn get_leaf_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let mut store = MerkleStore::new(); - store.extend(mtree.inner_nodes()); + let store: MerkleStore = mtree.clone().into(); let depth = mtree.depth(); let root = mtree.root(); let size_u64 = size as u64; @@ -109,8 +108,7 @@ fn get_leaf_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let mut store = MerkleStore::new(); - store.extend(smt.inner_nodes()); + let store: MerkleStore = smt.clone().into(); let depth = smt.depth(); let root = smt.root(); let size_u64 = size as u64; @@ -143,7 +141,7 @@ fn get_node_of_empty_simplesmt(c: &mut Criterion) { // of these values is what is being benchmarked here, so no values are inserted into the // backends. let smt = SimpleSmt::new(depth).unwrap(); - let store = MerkleStore::new(); + let store: MerkleStore = smt.clone().into(); let root = smt.root(); let half_depth = depth / 2; let half_size = 2_u64.pow(half_depth as u32); @@ -178,8 +176,7 @@ fn get_node_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let mut store = MerkleStore::new(); - store.extend(mtree.inner_nodes()); + let store: MerkleStore = mtree.clone().into(); let root = mtree.root(); let half_depth = mtree.depth() / 2; let half_size = 2_u64.pow(half_depth as u32); @@ -222,8 +219,7 @@ fn get_node_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let mut store = MerkleStore::new(); - store.extend(smt.inner_nodes()); + let store: MerkleStore = smt.clone().into(); let root = smt.root(); let half_depth = smt.depth() / 2; let half_size = 2_u64.pow(half_depth as u32); @@ -258,8 +254,7 @@ fn get_leaf_path_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let mut store = MerkleStore::new(); - store.extend(mtree.inner_nodes()); + let store: MerkleStore = mtree.clone().into(); let depth = mtree.depth(); let root = mtree.root(); let size_u64 = size as u64; @@ -301,8 +296,7 @@ fn get_leaf_path_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let mut store = MerkleStore::new(); - store.extend(smt.inner_nodes()); + let store: MerkleStore = smt.clone().into(); let depth = smt.depth(); let root = smt.root(); let size_u64 = size as u64; @@ -352,7 +346,7 @@ fn new(c: &mut Criterion) { || leaves.iter().map(|v| v.into()).collect::>(), |l| { let mtree = MerkleTree::new(l).unwrap(); - black_box(MerkleStore::new().extend(mtree.inner_nodes())); + black_box(MerkleStore::from(mtree)); }, BatchSize::SmallInput, ) @@ -383,7 +377,7 @@ fn new(c: &mut Criterion) { }, |l| { let smt = SimpleSmt::new(SimpleSmt::MAX_DEPTH).unwrap().with_leaves(l).unwrap(); - black_box(MerkleStore::new().extend(smt.inner_nodes())); + black_box(MerkleStore::from(smt)); }, BatchSize::SmallInput, ) @@ -403,8 +397,7 @@ fn update_leaf_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mut mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let mut store = MerkleStore::new(); - store.extend(mtree.inner_nodes()); + let mut store: MerkleStore = mtree.clone().into(); let depth = mtree.depth(); let root = mtree.root(); let size_u64 = size as u64; @@ -453,8 +446,7 @@ fn update_leaf_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let mut store = MerkleStore::new(); - store.extend(smt.inner_nodes()); + let mut store: MerkleStore = smt.clone().into(); let depth = smt.depth(); let root = smt.root(); let size_u64 = size as u64; diff --git a/src/merkle/store/mod.rs b/src/merkle/store/mod.rs index 776da47..8ed03e7 100644 --- a/src/merkle/store/mod.rs +++ b/src/merkle/store/mod.rs @@ -1,7 +1,7 @@ use super::mmr::Mmr; use super::{ - BTreeMap, EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, MerklePathSet, NodeIndex, - RootPath, Rpo256, RpoDigest, ValuePath, Vec, Word, + BTreeMap, EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, MerklePathSet, MerkleTree, + NodeIndex, RootPath, Rpo256, RpoDigest, SimpleSmt, ValuePath, Vec, Word, }; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; @@ -393,6 +393,33 @@ impl MerkleStore { } } +// CONVERTIONS +// ================================================================================================ + +impl From for MerkleStore { + fn from(value: MerkleTree) -> Self { + let mut store = MerkleStore::new(); + store.extend(value.inner_nodes()); + store + } +} + +impl From for MerkleStore { + fn from(value: SimpleSmt) -> Self { + let mut store = MerkleStore::new(); + store.extend(value.inner_nodes()); + store + } +} + +impl From for MerkleStore { + fn from(value: Mmr) -> Self { + let mut store = MerkleStore::new(); + store.extend(value.inner_nodes()); + store + } +} + // ITERATORS // ================================================================================================ diff --git a/src/merkle/store/tests.rs b/src/merkle/store/tests.rs index 3df4f21..4c5eeb4 100644 --- a/src/merkle/store/tests.rs +++ b/src/merkle/store/tests.rs @@ -15,8 +15,7 @@ const EMPTY: Word = [ZERO; WORD_SIZE]; #[test] fn test_root_not_in_store() -> Result<(), MerkleError> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let mut store = MerkleStore::new(); - store.extend(mtree.inner_nodes()); + let store: MerkleStore = mtree.clone().into(); assert_eq!( store.get_node(LEAVES4[0], NodeIndex::make(mtree.depth(), 0)), Err(MerkleError::RootNotInStore(LEAVES4[0])), @@ -34,8 +33,7 @@ fn test_root_not_in_store() -> Result<(), MerkleError> { #[test] fn test_merkle_tree() -> Result<(), MerkleError> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let mut store = MerkleStore::default(); - store.extend(mtree.inner_nodes()); + let store: MerkleStore = mtree.clone().into(); // STORE LEAVES ARE CORRECT ============================================================== // checks the leaves in the store corresponds to the expected values @@ -177,8 +175,7 @@ fn test_leaf_paths_for_empty_trees() -> Result<(), MerkleError> { #[test] fn test_get_invalid_node() { let mtree = MerkleTree::new(LEAVES4.to_vec()).expect("creating a merkle tree must work"); - let mut store = MerkleStore::default(); - store.extend(mtree.inner_nodes()); + let store: MerkleStore = mtree.clone().into(); let _ = store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 3)); } @@ -190,8 +187,7 @@ fn test_add_sparse_merkle_tree_one_level() -> Result<(), MerkleError> { .unwrap() .with_leaves(keys2.into_iter().zip(leaves2.into_iter())) .unwrap(); - let mut store = MerkleStore::default(); - store.extend(smt.inner_nodes()); + let store: MerkleStore = smt.clone().into(); let idx = NodeIndex::make(1, 0); assert_eq!(smt.get_node(idx).unwrap(), leaves2[0]); @@ -211,8 +207,7 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> { .with_leaves(KEYS4.into_iter().zip(LEAVES4.into_iter())) .unwrap(); - let mut store = MerkleStore::default(); - store.extend(smt.inner_nodes()); + let store: MerkleStore = smt.clone().into(); // STORE LEAVES ARE CORRECT ============================================================== // checks the leaves in the store corresponds to the expected values @@ -470,8 +465,7 @@ fn wont_open_to_different_depth_root() { // attempt to fetch a node on the maximum depth, and it should fail because the root shouldn't // exist for the set. let mtree = MerkleTree::new(vec![a, b]).unwrap(); - let mut store = MerkleStore::new(); - store.extend(mtree.inner_nodes()); + let store: MerkleStore = mtree.clone().into(); let index = NodeIndex::root(); let err = store.get_node(root, index).err().unwrap(); assert_eq!(err, MerkleError::RootNotInStore(root)); @@ -499,8 +493,7 @@ fn store_path_opens_from_leaf() { let root = Rpo256::merge(&[m.into(), n.into()]); let mtree = MerkleTree::new(vec![a, b, c, d, e, f, g, h]).unwrap(); - let mut store = MerkleStore::new(); - store.extend(mtree.inner_nodes()); + let store: MerkleStore = mtree.clone().into(); let path = store.get_path(root.into(), NodeIndex::make(3, 1)).unwrap().path; let expected = MerklePath::new([a.into(), j.into(), n.into()].to_vec()); @@ -510,8 +503,7 @@ fn store_path_opens_from_leaf() { #[test] fn test_set_node() -> Result<(), MerkleError> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let mut store = MerkleStore::new(); - store.extend(mtree.inner_nodes()); + let mut store: MerkleStore = mtree.clone().into(); let value = int_to_node(42); let index = NodeIndex::make(mtree.depth(), 0); let new_root = store.set_node(mtree.root(), index, value)?.root; @@ -523,8 +515,7 @@ fn test_set_node() -> Result<(), MerkleError> { #[test] fn test_constructors() -> Result<(), MerkleError> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let mut store = MerkleStore::new(); - store.extend(mtree.inner_nodes()); + let store: MerkleStore = mtree.clone().into(); let depth = mtree.depth(); let leaves = 2u64.pow(depth.into()); @@ -539,8 +530,7 @@ fn test_constructors() -> Result<(), MerkleError> { .unwrap() .with_leaves(KEYS4.into_iter().zip(LEAVES4.into_iter())) .unwrap(); - let mut store = MerkleStore::new(); - store.extend(smt.inner_nodes()); + let store: MerkleStore = smt.clone().into(); let depth = smt.depth(); for key in KEYS4 { @@ -730,8 +720,7 @@ fn get_leaf_depth_works_with_depth_8() { #[test] fn test_serialization() -> Result<(), Box> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let mut store = MerkleStore::new(); - store.extend(mtree.inner_nodes()); + let store: MerkleStore = mtree.clone().into(); let decoded = MerkleStore::read_from_bytes(&original.to_bytes())?; assert_eq!(original, decoded); Ok(()) From 9be4253f195a2e2249a1b451dd3984a316616e13 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 21 Apr 2023 11:22:36 -0700 Subject: [PATCH 08/11] feat: remove clone requirement for MerkleStore From constructors --- benches/store.rs | 24 ++++++++++++------------ src/merkle/merkle_tree.rs | 3 +++ src/merkle/store/mod.rs | 22 +++++++++++++++------- src/merkle/store/tests.rs | 20 ++++++++++---------- 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/benches/store.rs b/benches/store.rs index 2319128..3ad79c4 100644 --- a/benches/store.rs +++ b/benches/store.rs @@ -34,7 +34,7 @@ fn get_empty_leaf_simplesmt(c: &mut Criterion) { // both SMT and the store are pre-populated with empty hashes, accessing these values is what is // being benchmarked here, so no values are inserted into the backends let smt = SimpleSmt::new(depth).unwrap(); - let store: MerkleStore = smt.clone().into(); + let store = MerkleStore::from(&smt); let root = smt.root(); group.bench_function(BenchmarkId::new("SimpleSmt", depth), |b| { @@ -66,7 +66,7 @@ fn get_leaf_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let store: MerkleStore = mtree.clone().into(); + let store = MerkleStore::from(&mtree); let depth = mtree.depth(); let root = mtree.root(); let size_u64 = size as u64; @@ -108,7 +108,7 @@ fn get_leaf_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let store: MerkleStore = smt.clone().into(); + let store = MerkleStore::from(&smt); let depth = smt.depth(); let root = smt.root(); let size_u64 = size as u64; @@ -141,7 +141,7 @@ fn get_node_of_empty_simplesmt(c: &mut Criterion) { // of these values is what is being benchmarked here, so no values are inserted into the // backends. let smt = SimpleSmt::new(depth).unwrap(); - let store: MerkleStore = smt.clone().into(); + let store = MerkleStore::from(&smt); let root = smt.root(); let half_depth = depth / 2; let half_size = 2_u64.pow(half_depth as u32); @@ -176,7 +176,7 @@ fn get_node_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let store: MerkleStore = mtree.clone().into(); + let store = MerkleStore::from(&mtree); let root = mtree.root(); let half_depth = mtree.depth() / 2; let half_size = 2_u64.pow(half_depth as u32); @@ -219,7 +219,7 @@ fn get_node_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let store: MerkleStore = smt.clone().into(); + let store = MerkleStore::from(&smt); let root = smt.root(); let half_depth = smt.depth() / 2; let half_size = 2_u64.pow(half_depth as u32); @@ -254,7 +254,7 @@ fn get_leaf_path_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let store: MerkleStore = mtree.clone().into(); + let store = MerkleStore::from(&mtree); let depth = mtree.depth(); let root = mtree.root(); let size_u64 = size as u64; @@ -296,7 +296,7 @@ fn get_leaf_path_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let store: MerkleStore = smt.clone().into(); + let store = MerkleStore::from(&smt); let depth = smt.depth(); let root = smt.root(); let size_u64 = size as u64; @@ -346,7 +346,7 @@ fn new(c: &mut Criterion) { || leaves.iter().map(|v| v.into()).collect::>(), |l| { let mtree = MerkleTree::new(l).unwrap(); - black_box(MerkleStore::from(mtree)); + black_box(MerkleStore::from(&mtree)); }, BatchSize::SmallInput, ) @@ -377,7 +377,7 @@ fn new(c: &mut Criterion) { }, |l| { let smt = SimpleSmt::new(SimpleSmt::MAX_DEPTH).unwrap().with_leaves(l).unwrap(); - black_box(MerkleStore::from(smt)); + black_box(MerkleStore::from(&smt)); }, BatchSize::SmallInput, ) @@ -397,7 +397,7 @@ fn update_leaf_merkletree(c: &mut Criterion) { let mtree_leaves: Vec = leaves.iter().map(|v| v.into()).collect(); let mut mtree = MerkleTree::new(mtree_leaves.clone()).unwrap(); - let mut store: MerkleStore = mtree.clone().into(); + let mut store = MerkleStore::from(&mtree); let depth = mtree.depth(); let root = mtree.root(); let size_u64 = size as u64; @@ -446,7 +446,7 @@ fn update_leaf_simplesmt(c: &mut Criterion) { .unwrap() .with_leaves(smt_leaves.clone()) .unwrap(); - let mut store: MerkleStore = smt.clone().into(); + let mut store = MerkleStore::from(&smt); let depth = smt.depth(); let root = smt.root(); let size_u64 = size as u64; diff --git a/src/merkle/merkle_tree.rs b/src/merkle/merkle_tree.rs index 4e39a09..106ec27 100644 --- a/src/merkle/merkle_tree.rs +++ b/src/merkle/merkle_tree.rs @@ -192,6 +192,9 @@ impl<'a> Iterator for MerkleTreeNodes<'a> { } } +// UTILITY FUNCTIONS +// ================================================================================================ + /// Utility to visualize a [MerkleTree] in text. pub fn tree_to_text(tree: &MerkleTree) -> Result { let indent = " "; diff --git a/src/merkle/store/mod.rs b/src/merkle/store/mod.rs index 8ed03e7..442ff00 100644 --- a/src/merkle/store/mod.rs +++ b/src/merkle/store/mod.rs @@ -393,33 +393,41 @@ impl MerkleStore { } } -// CONVERTIONS +// CONVERSIONS // ================================================================================================ -impl From for MerkleStore { - fn from(value: MerkleTree) -> Self { +impl From<&MerkleTree> for MerkleStore { + fn from(value: &MerkleTree) -> Self { let mut store = MerkleStore::new(); store.extend(value.inner_nodes()); store } } -impl From for MerkleStore { - fn from(value: SimpleSmt) -> Self { +impl From<&SimpleSmt> for MerkleStore { + fn from(value: &SimpleSmt) -> Self { let mut store = MerkleStore::new(); store.extend(value.inner_nodes()); store } } -impl From for MerkleStore { - fn from(value: Mmr) -> Self { +impl From<&Mmr> for MerkleStore { + fn from(value: &Mmr) -> Self { let mut store = MerkleStore::new(); store.extend(value.inner_nodes()); store } } +impl FromIterator for MerkleStore { + fn from_iter>(iter: T) -> Self { + let mut store = MerkleStore::new(); + store.extend(iter.into_iter()); + store + } +} + // ITERATORS // ================================================================================================ diff --git a/src/merkle/store/tests.rs b/src/merkle/store/tests.rs index 4c5eeb4..ca5be9e 100644 --- a/src/merkle/store/tests.rs +++ b/src/merkle/store/tests.rs @@ -15,7 +15,7 @@ const EMPTY: Word = [ZERO; WORD_SIZE]; #[test] fn test_root_not_in_store() -> Result<(), MerkleError> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let store: MerkleStore = mtree.clone().into(); + let store = MerkleStore::from(&mtree); assert_eq!( store.get_node(LEAVES4[0], NodeIndex::make(mtree.depth(), 0)), Err(MerkleError::RootNotInStore(LEAVES4[0])), @@ -33,7 +33,7 @@ fn test_root_not_in_store() -> Result<(), MerkleError> { #[test] fn test_merkle_tree() -> Result<(), MerkleError> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let store: MerkleStore = mtree.clone().into(); + let store = MerkleStore::from(&mtree); // STORE LEAVES ARE CORRECT ============================================================== // checks the leaves in the store corresponds to the expected values @@ -175,7 +175,7 @@ fn test_leaf_paths_for_empty_trees() -> Result<(), MerkleError> { #[test] fn test_get_invalid_node() { let mtree = MerkleTree::new(LEAVES4.to_vec()).expect("creating a merkle tree must work"); - let store: MerkleStore = mtree.clone().into(); + let store = MerkleStore::from(&mtree); let _ = store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 3)); } @@ -187,7 +187,7 @@ fn test_add_sparse_merkle_tree_one_level() -> Result<(), MerkleError> { .unwrap() .with_leaves(keys2.into_iter().zip(leaves2.into_iter())) .unwrap(); - let store: MerkleStore = smt.clone().into(); + let store = MerkleStore::from(&smt); let idx = NodeIndex::make(1, 0); assert_eq!(smt.get_node(idx).unwrap(), leaves2[0]); @@ -207,7 +207,7 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> { .with_leaves(KEYS4.into_iter().zip(LEAVES4.into_iter())) .unwrap(); - let store: MerkleStore = smt.clone().into(); + let store = MerkleStore::from(&smt); // STORE LEAVES ARE CORRECT ============================================================== // checks the leaves in the store corresponds to the expected values @@ -465,7 +465,7 @@ fn wont_open_to_different_depth_root() { // attempt to fetch a node on the maximum depth, and it should fail because the root shouldn't // exist for the set. let mtree = MerkleTree::new(vec![a, b]).unwrap(); - let store: MerkleStore = mtree.clone().into(); + let store = MerkleStore::from(&mtree); let index = NodeIndex::root(); let err = store.get_node(root, index).err().unwrap(); assert_eq!(err, MerkleError::RootNotInStore(root)); @@ -493,7 +493,7 @@ fn store_path_opens_from_leaf() { let root = Rpo256::merge(&[m.into(), n.into()]); let mtree = MerkleTree::new(vec![a, b, c, d, e, f, g, h]).unwrap(); - let store: MerkleStore = mtree.clone().into(); + let store = MerkleStore::from(&mtree); let path = store.get_path(root.into(), NodeIndex::make(3, 1)).unwrap().path; let expected = MerklePath::new([a.into(), j.into(), n.into()].to_vec()); @@ -503,7 +503,7 @@ fn store_path_opens_from_leaf() { #[test] fn test_set_node() -> Result<(), MerkleError> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let mut store: MerkleStore = mtree.clone().into(); + let mut store = MerkleStore::from(&mtree); let value = int_to_node(42); let index = NodeIndex::make(mtree.depth(), 0); let new_root = store.set_node(mtree.root(), index, value)?.root; @@ -515,7 +515,7 @@ fn test_set_node() -> Result<(), MerkleError> { #[test] fn test_constructors() -> Result<(), MerkleError> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let store: MerkleStore = mtree.clone().into(); + let store = MerkleStore::from(&mtree); let depth = mtree.depth(); let leaves = 2u64.pow(depth.into()); @@ -530,7 +530,7 @@ fn test_constructors() -> Result<(), MerkleError> { .unwrap() .with_leaves(KEYS4.into_iter().zip(LEAVES4.into_iter())) .unwrap(); - let store: MerkleStore = smt.clone().into(); + let store = MerkleStore::from(&smt); let depth = smt.depth(); for key in KEYS4 { From 22c9f382c497462bdca7e35a989f9dc60a3caa06 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 21 Apr 2023 11:39:49 -0700 Subject: [PATCH 09/11] fix: serialization test --- src/merkle/store/tests.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/merkle/store/tests.rs b/src/merkle/store/tests.rs index ca5be9e..28ac8d7 100644 --- a/src/merkle/store/tests.rs +++ b/src/merkle/store/tests.rs @@ -5,7 +5,7 @@ use crate::{ Felt, Word, WORD_SIZE, ZERO, }; -#[cfg(std)] +#[cfg(feature = "std")] use std::error::Error; const KEYS4: [u64; 4] = [0, 1, 2, 3]; @@ -716,12 +716,12 @@ fn get_leaf_depth_works_with_depth_8() { assert_eq!(Err(MerkleError::DepthTooBig(9)), store.get_leaf_depth(root, 8, a)); } -#[cfg(std)] +#[cfg(feature = "std")] #[test] fn test_serialization() -> Result<(), Box> { let mtree = MerkleTree::new(LEAVES4.to_vec())?; - let store: MerkleStore = mtree.clone().into(); - let decoded = MerkleStore::read_from_bytes(&original.to_bytes())?; - assert_eq!(original, decoded); + let store = MerkleStore::from(&mtree); + let decoded = MerkleStore::read_from_bytes(&store.to_bytes()).expect("deserialization failed"); + assert_eq!(store, decoded); Ok(()) } From 130ae3d12a54cae50ffc49f59456236146719060 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 21 Apr 2023 14:27:58 -0700 Subject: [PATCH 10/11] feat: add inner node iterator to MerklePath --- src/merkle/merkle_tree.rs | 12 +++--- src/merkle/path.rs | 84 ++++++++++++++++++++++++++++++++++++--- src/merkle/store/mod.rs | 59 ++++++++------------------- src/merkle/store/tests.rs | 14 ++++--- 4 files changed, 109 insertions(+), 60 deletions(-) diff --git a/src/merkle/merkle_tree.rs b/src/merkle/merkle_tree.rs index 106ec27..42f289a 100644 --- a/src/merkle/merkle_tree.rs +++ b/src/merkle/merkle_tree.rs @@ -150,9 +150,11 @@ impl MerkleTree { Ok(()) } - /// An iterator over every inner node in the tree. The iterator order is unspecified. - pub fn inner_nodes(&self) -> MerkleTreeNodes<'_> { - MerkleTreeNodes { + /// Returns n iterator over every inner node of this [MerkleTree]. + /// + /// The iterator order is unspecified. + pub fn inner_nodes(&self) -> InnerNodeIterator<'_> { + InnerNodeIterator { nodes: &self.nodes, index: 1, // index 0 is just padding, start at 1 } @@ -165,12 +167,12 @@ impl MerkleTree { /// An iterator over every inner node of the [MerkleTree]. /// /// Use this to extract the data of the tree, there is no guarantee on the order of the elements. -pub struct MerkleTreeNodes<'a> { +pub struct InnerNodeIterator<'a> { nodes: &'a Vec, index: usize, } -impl<'a> Iterator for MerkleTreeNodes<'a> { +impl<'a> Iterator for InnerNodeIterator<'a> { type Item = InnerNodeInfo; fn next(&mut self) -> Option { diff --git a/src/merkle/path.rs b/src/merkle/path.rs index 964f6d0..e873d84 100644 --- a/src/merkle/path.rs +++ b/src/merkle/path.rs @@ -1,4 +1,4 @@ -use super::{vec, MerkleError, NodeIndex, Rpo256, Vec, Word}; +use super::{vec, InnerNodeInfo, MerkleError, NodeIndex, Rpo256, Vec, Word}; use core::ops::{Deref, DerefMut}; // MERKLE PATH @@ -22,6 +22,11 @@ impl MerklePath { // PROVIDERS // -------------------------------------------------------------------------------------------- + /// Returns the depth in which this Merkle path proof is valid. + pub fn depth(&self) -> u8 { + self.nodes.len() as u8 + } + /// Computes the merkle root for this opening. pub fn compute_root(&self, index: u64, node: Word) -> Result { let mut index = NodeIndex::new(self.depth(), index)?; @@ -34,11 +39,6 @@ impl MerklePath { Ok(root) } - /// Returns the depth in which this Merkle path proof is valid. - pub fn depth(&self) -> u8 { - self.nodes.len() as u8 - } - /// Verifies the Merkle opening proof towards the provided root. /// /// Returns `true` if `node` exists at `index` in a Merkle tree with `root`. @@ -48,6 +48,20 @@ impl MerklePath { Err(_) => false, } } + + /// Returns an iterator over every inner node of this [MerklePath]. + /// + /// The iteration order is unspecified. + /// + /// # Errors + /// Returns an error if the specified index is not valid for this path. + pub fn inner_nodes(&self, index: u64, node: Word) -> Result { + Ok(InnerNodeIterator { + nodes: &self.nodes, + index: NodeIndex::new(self.depth(), index)?, + value: node, + }) + } } impl From> for MerklePath { @@ -72,6 +86,9 @@ impl DerefMut for MerklePath { } } +// ITERATORS +// ================================================================================================ + impl FromIterator for MerklePath { fn from_iter>(iter: T) -> Self { Self::new(iter.into_iter().collect()) @@ -87,6 +104,39 @@ impl IntoIterator for MerklePath { } } +/// An iterator over internal nodes of a [MerklePath]. +pub struct InnerNodeIterator<'a> { + nodes: &'a Vec, + index: NodeIndex, + value: Word, +} + +impl<'a> Iterator for InnerNodeIterator<'a> { + type Item = InnerNodeInfo; + + fn next(&mut self) -> Option { + if !self.index.is_root() { + let sibling_pos = self.nodes.len() - self.index.depth() as usize; + let (left, right) = if self.index.is_value_odd() { + (self.nodes[sibling_pos], self.value) + } else { + (self.value, self.nodes[sibling_pos]) + }; + + self.value = Rpo256::merge(&[left.into(), right.into()]).into(); + self.index.move_up(); + + Some(InnerNodeInfo { + value: self.value, + left, + right, + }) + } else { + None + } + } +} + // MERKLE PATH CONTAINERS // ================================================================================================ @@ -110,3 +160,25 @@ pub struct RootPath { /// The path from `value` to `root` (exclusive). pub path: MerklePath, } + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use crate::merkle::{int_to_node, MerklePath}; + + #[test] + fn test_inner_nodes() { + let nodes = vec![int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)]; + let merkle_path = MerklePath::new(nodes); + + let index = 6; + let node = int_to_node(5); + let root = merkle_path.compute_root(index, node).unwrap(); + + let inner_root = merkle_path.inner_nodes(index, node).unwrap().last().unwrap().value; + + assert_eq!(root, inner_root); + } +} diff --git a/src/merkle/store/mod.rs b/src/merkle/store/mod.rs index 442ff00..96ca54a 100644 --- a/src/merkle/store/mod.rs +++ b/src/merkle/store/mod.rs @@ -115,26 +115,6 @@ impl MerkleStore { MerkleStore { nodes } } - /// Appends the provided merkle path set. - pub fn with_merkle_path( - mut self, - index_value: u64, - node: Word, - path: MerklePath, - ) -> Result { - self.add_merkle_path(index_value, node, path)?; - Ok(self) - } - - /// Appends the provided merkle path set. - pub fn with_merkle_paths(mut self, paths: I) -> Result - where - I: IntoIterator, - { - self.add_merkle_paths(paths)?; - Ok(self) - } - // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- @@ -280,8 +260,11 @@ impl MerkleStore { // STATE MUTATORS // -------------------------------------------------------------------------------------------- - /// Adds a new [InnerNodeInfo] into the store. - pub fn extend(&mut self, iter: impl Iterator) -> &mut MerkleStore { + /// Adds a sequence of nodes yielded by the provided iterator into the store. + pub fn extend(&mut self, iter: I) -> &mut MerkleStore + where + I: Iterator, + { for node in iter { let value: RpoDigest = node.value.into(); let left: RpoDigest = node.left.into(); @@ -301,31 +284,21 @@ impl MerkleStore { /// include all the nodes into the store. pub fn add_merkle_path( &mut self, - index_value: u64, - mut node: Word, + index: u64, + node: Word, path: MerklePath, ) -> Result { - let mut index = NodeIndex::new(path.len() as u8, index_value)?; + let root = path.inner_nodes(index, node)?.fold(Word::default(), |_, node| { + let value: RpoDigest = node.value.into(); + let left: RpoDigest = node.left.into(); + let right: RpoDigest = node.right.into(); - for sibling in path { - let (left, right) = match index.is_value_odd() { - true => (sibling, node), - false => (node, sibling), - }; - let parent = Rpo256::merge(&[left.into(), right.into()]); - self.nodes.insert( - parent, - Node { - left: left.into(), - right: right.into(), - }, - ); - - index.move_up(); - node = parent.into(); - } + debug_assert_eq!(Rpo256::merge(&[left, right]), value); + self.nodes.insert(value, Node { left, right }); - Ok(node) + node.value + }); + Ok(root) } /// Adds all the nodes of multiple Merkle paths into the store. diff --git a/src/merkle/store/tests.rs b/src/merkle/store/tests.rs index 28ac8d7..02e12b2 100644 --- a/src/merkle/store/tests.rs +++ b/src/merkle/store/tests.rs @@ -547,12 +547,14 @@ fn test_constructors() -> Result<(), MerkleError> { (3, LEAVES4[3], mtree.get_path(NodeIndex::make(d, 3)).unwrap()), ]; - let store1 = MerkleStore::default().with_merkle_paths(paths.clone())?; - let store2 = MerkleStore::default() - .with_merkle_path(0, LEAVES4[0], mtree.get_path(NodeIndex::make(d, 0))?)? - .with_merkle_path(1, LEAVES4[1], mtree.get_path(NodeIndex::make(d, 1))?)? - .with_merkle_path(2, LEAVES4[2], mtree.get_path(NodeIndex::make(d, 2))?)? - .with_merkle_path(3, LEAVES4[3], mtree.get_path(NodeIndex::make(d, 3))?)?; + let mut store1 = MerkleStore::default(); + store1.add_merkle_paths(paths.clone())?; + + let mut store2 = MerkleStore::default(); + store2.add_merkle_path(0, LEAVES4[0], mtree.get_path(NodeIndex::make(d, 0))?)?; + store2.add_merkle_path(1, LEAVES4[1], mtree.get_path(NodeIndex::make(d, 1))?)?; + store2.add_merkle_path(2, LEAVES4[2], mtree.get_path(NodeIndex::make(d, 2))?)?; + store2.add_merkle_path(3, LEAVES4[3], mtree.get_path(NodeIndex::make(d, 3))?)?; let set = MerklePathSet::new(d).with_paths(paths).unwrap(); for key in [0, 1, 2, 3] { From e983e940b224ad04d36c65571aef272c875486e9 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 21 Apr 2023 14:42:08 -0700 Subject: [PATCH 11/11] chore: update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79ca146..ff66e1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ -## 0.4.0 (TBD) +## 0.4.0 (2023-04-21) +- Exported `MmrProof` from the crate (#137). +- Allowed merging of leaves in `MerkleStore` (#138). +- [BREAKING] Refactored how existing data structures are added to `MerkleStore` (#139). ## 0.3.0 (2023-04-08)