use alloc::{ collections::{BTreeMap, BTreeSet}, vec::Vec, }; use winter_utils::{Deserializable, Serializable}; use super::{MmrDelta, MmrProof, Rpo256, RpoDigest}; use crate::merkle::{ mmr::{leaf_to_corresponding_tree, nodes_in_forest}, InOrderIndex, InnerNodeInfo, MerklePath, MmrError, MmrPeaks, }; // TYPE ALIASES // ================================================================================================ type NodeMap = BTreeMap; // PARTIAL MERKLE MOUNTAIN RANGE // ================================================================================================ /// Partially materialized Merkle Mountain Range (MMR), used to efficiently store and update the /// authentication paths for a subset of the elements in a full MMR. /// /// This structure store only the authentication path for a value, the value itself is stored /// separately. #[derive(Debug, Clone, PartialEq, Eq)] pub struct PartialMmr { /// The version of the MMR. /// /// This value serves the following purposes: /// /// - The forest is a counter for the total number of elements in the MMR. /// - Since the MMR is an append-only structure, every change to it causes a change to the /// `forest`, so this value has a dual purpose as a version tag. /// - The bits in the forest also corresponds to the count and size of every perfect binary /// tree that composes the MMR structure, which server to compute indexes and perform /// validation. pub(crate) forest: usize, /// The MMR peaks. /// /// The peaks are used for two reasons: /// /// 1. It authenticates the addition of an element to the [PartialMmr], ensuring only valid /// elements are tracked. /// 2. During a MMR update peaks can be merged by hashing the left and right hand sides. The /// peaks are used as the left hand. /// /// All the peaks of every tree in the MMR forest. The peaks are always ordered by number of /// leaves, starting from the peak with most children, to the one with least. pub(crate) peaks: Vec, /// Authentication nodes used to construct merkle paths for a subset of the MMR's leaves. /// /// This does not include the MMR's peaks nor the tracked nodes, only the elements required to /// construct their authentication paths. This property is used to detect when elements can be /// safely removed, because they are no longer required to authenticate any element in the /// [PartialMmr]. /// /// The elements in the MMR are referenced using a in-order tree index. This indexing scheme /// permits for easy computation of the relative nodes (left/right children, sibling, parent), /// which is useful for traversal. The indexing is also stable, meaning that merges to the /// trees in the MMR can be represented without rewrites of the indexes. pub(crate) nodes: NodeMap, /// Flag indicating if the odd element should be tracked. /// /// This flag is necessary because the sibling of the odd doesn't exist yet, so it can not be /// added into `nodes` to signal the value is being tracked. pub(crate) track_latest: bool, } impl PartialMmr { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- /// Returns a new [PartialMmr] instantiated from the specified peaks. pub fn from_peaks(peaks: MmrPeaks) -> Self { let forest = peaks.num_leaves(); let peaks = peaks.into(); let nodes = BTreeMap::new(); let track_latest = false; Self { forest, peaks, nodes, track_latest } } /// Returns a new [PartialMmr] instantiated from the specified components. /// /// This constructor does not check the consistency between peaks and nodes. If the specified /// peaks are nodes are inconsistent, the returned partial MMR may exhibit undefined behavior. pub fn from_parts(peaks: MmrPeaks, nodes: NodeMap, track_latest: bool) -> Self { let forest = peaks.num_leaves(); let peaks = peaks.into(); Self { forest, peaks, nodes, track_latest } } // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- /// Returns the current `forest` of this [PartialMmr]. /// /// This value corresponds to the version of the [PartialMmr] and the number of leaves in the /// underlying MMR. pub fn forest(&self) -> usize { self.forest } /// Returns the number of leaves in the underlying MMR for this [PartialMmr]. pub fn num_leaves(&self) -> usize { self.forest } /// Returns the peaks of the MMR for this [PartialMmr]. pub fn peaks(&self) -> MmrPeaks { // expect() is OK here because the constructor ensures that MMR peaks can be constructed // correctly MmrPeaks::new(self.forest, self.peaks.clone()).expect("invalid MMR peaks") } /// Returns true if this partial MMR tracks an authentication path for the leaf at the /// specified position. pub fn is_tracked(&self, pos: usize) -> bool { if pos >= self.forest { return false; } else if pos == self.forest - 1 && self.forest & 1 != 0 { // if the number of leaves in the MMR is odd and the position is for the last leaf // whether the leaf is tracked is defined by the `track_latest` flag return self.track_latest; } let leaf_index = InOrderIndex::from_leaf_pos(pos); self.is_tracked_node(&leaf_index) } /// Given a leaf position, returns the Merkle path to its corresponding peak, or None if this /// partial MMR does not track an authentication paths for the specified leaf. /// /// Note: The leaf position is the 0-indexed number corresponding to the order the leaves were /// added, this corresponds to the MMR size _prior_ to adding the element. So the 1st element /// has position 0, the second position 1, and so on. /// /// # Errors /// Returns an error if the specified position is greater-or-equal than the number of leaves /// in the underlying MMR. pub fn open(&self, pos: usize) -> Result, MmrError> { let tree_bit = leaf_to_corresponding_tree(pos, self.forest).ok_or(MmrError::InvalidPosition(pos))?; let depth = tree_bit as usize; let mut nodes = Vec::with_capacity(depth); let mut idx = InOrderIndex::from_leaf_pos(pos); while let Some(node) = self.nodes.get(&idx.sibling()) { nodes.push(*node); idx = idx.parent(); } // If there are nodes then the path must be complete, otherwise it is a bug debug_assert!(nodes.is_empty() || nodes.len() == depth); if nodes.len() != depth { // The requested `pos` is not being tracked. Ok(None) } else { Ok(Some(MmrProof { forest: self.forest, position: pos, merkle_path: MerklePath::new(nodes), })) } } // ITERATORS // -------------------------------------------------------------------------------------------- /// Returns an iterator nodes of all authentication paths of this [PartialMmr]. pub fn nodes(&self) -> impl Iterator { self.nodes.iter() } /// Returns an iterator over inner nodes of this [PartialMmr] for the specified leaves. /// /// The order of iteration is not defined. If a leaf is not presented in this partial MMR it /// is silently ignored. pub fn inner_nodes<'a, I: Iterator + 'a>( &'a self, mut leaves: I, ) -> impl Iterator + 'a { let stack = if let Some((pos, leaf)) = leaves.next() { let idx = InOrderIndex::from_leaf_pos(pos); vec![(idx, leaf)] } else { Vec::new() }; InnerNodeIterator { nodes: &self.nodes, leaves, stack, seen_nodes: BTreeSet::new(), } } // STATE MUTATORS // -------------------------------------------------------------------------------------------- /// Adds a new peak and optionally track it. Returns a vector of the authentication nodes /// inserted into this [PartialMmr] as a result of this operation. /// /// When `track` is `true` the new leaf is tracked. pub fn add(&mut self, leaf: RpoDigest, track: bool) -> Vec<(InOrderIndex, RpoDigest)> { self.forest += 1; let merges = self.forest.trailing_zeros() as usize; let mut new_nodes = Vec::with_capacity(merges); let peak = if merges == 0 { self.track_latest = track; leaf } else { let mut track_right = track; let mut track_left = self.track_latest; let mut right = leaf; let mut right_idx = forest_to_rightmost_index(self.forest); for _ in 0..merges { let left = self.peaks.pop().expect("Missing peak"); let left_idx = right_idx.sibling(); if track_right { let old = self.nodes.insert(left_idx, left); new_nodes.push((left_idx, left)); debug_assert!( old.is_none(), "Idx {:?} already contained an element {:?}", left_idx, old ); }; if track_left { let old = self.nodes.insert(right_idx, right); new_nodes.push((right_idx, right)); debug_assert!( old.is_none(), "Idx {:?} already contained an element {:?}", right_idx, old ); }; // Update state for the next iteration. // -------------------------------------------------------------------------------- // This layer is merged, go up one layer. right_idx = right_idx.parent(); // Merge the current layer. The result is either the right element of the next // merge, or a new peak. right = Rpo256::merge(&[left, right]); // This iteration merged the left and right nodes, the new value is always used as // the next iteration's right node. Therefore the tracking flags of this iteration // have to be merged into the right side only. track_right = track_right || track_left; // On the next iteration, a peak will be merged. If any of its children are tracked, // then we have to track the left side track_left = self.is_tracked_node(&right_idx.sibling()); } right }; self.peaks.push(peak); new_nodes } /// Adds the authentication path represented by [MerklePath] if it is valid. /// /// The `leaf_pos` refers to the global position of the leaf in the MMR, these are 0-indexed /// values assigned in a strictly monotonic fashion as elements are inserted into the MMR, /// this value corresponds to the values used in the MMR structure. /// /// The `leaf` corresponds to the value at `leaf_pos`, and `path` is the authentication path for /// that element up to its corresponding Mmr peak. The `leaf` is only used to compute the root /// from the authentication path to valid the data, only the authentication data is saved in /// the structure. If the value is required it should be stored out-of-band. pub fn track( &mut self, leaf_pos: usize, leaf: RpoDigest, path: &MerklePath, ) -> Result<(), MmrError> { // Checks there is a tree with same depth as the authentication path, if not the path is // invalid. let tree = 1 << path.depth(); if tree & self.forest == 0 { return Err(MmrError::UnknownPeak); }; if leaf_pos + 1 == self.forest && path.depth() == 0 && self.peaks.last().map_or(false, |v| *v == leaf) { self.track_latest = true; return Ok(()); } // ignore the trees smaller than the target (these elements are position after the current // target and don't affect the target leaf_pos) let target_forest = self.forest ^ (self.forest & (tree - 1)); let peak_pos = (target_forest.count_ones() - 1) as usize; // translate from mmr leaf_pos to merkle path let path_idx = leaf_pos - (target_forest ^ tree); // Compute the root of the authentication path, and check it matches the current version of // the PartialMmr. let computed = path.compute_root(path_idx as u64, leaf).map_err(MmrError::MerkleError)?; if self.peaks[peak_pos] != computed { return Err(MmrError::InvalidPeak); } let mut idx = InOrderIndex::from_leaf_pos(leaf_pos); for leaf in path.nodes() { self.nodes.insert(idx.sibling(), *leaf); idx = idx.parent(); } Ok(()) } /// Removes a leaf of the [PartialMmr] and the unused nodes from the authentication path. /// /// Note: `leaf_pos` corresponds to the position in the MMR and not on an individual tree. pub fn untrack(&mut self, leaf_pos: usize) { let mut idx = InOrderIndex::from_leaf_pos(leaf_pos); self.nodes.remove(&idx.sibling()); // `idx` represent the element that can be computed by the authentication path, because // these elements can be computed they are not saved for the authentication of the current // target. In other words, if the idx is present it was added for the authentication of // another element, and no more elements should be removed otherwise it would remove that // element's authentication data. while !self.nodes.contains_key(&idx) { idx = idx.parent(); self.nodes.remove(&idx.sibling()); } } /// Applies updates to this [PartialMmr] and returns a vector of new authentication nodes /// inserted into the partial MMR. pub fn apply(&mut self, delta: MmrDelta) -> Result, MmrError> { if delta.forest < self.forest { return Err(MmrError::InvalidPeaks); } let mut inserted_nodes = Vec::new(); if delta.forest == self.forest { if !delta.data.is_empty() { return Err(MmrError::InvalidUpdate); } return Ok(inserted_nodes); } // find the tree merges let changes = self.forest ^ delta.forest; let largest = 1 << changes.ilog2(); let merges = self.forest & (largest - 1); debug_assert!( !self.track_latest || (merges & 1) == 1, "if there is an odd element, a merge is required" ); // count the number elements needed to produce largest from the current state let (merge_count, new_peaks) = if merges != 0 { let depth = largest.trailing_zeros(); let skipped = merges.trailing_zeros(); let computed = merges.count_ones() - 1; let merge_count = depth - skipped - computed; let new_peaks = delta.forest & (largest - 1); (merge_count, new_peaks) } else { (0, changes) }; // verify the delta size if (delta.data.len() as u32) != merge_count + new_peaks.count_ones() { return Err(MmrError::InvalidUpdate); } // keeps track of how many data elements from the update have been consumed let mut update_count = 0; if merges != 0 { // starts at the smallest peak and follows the merged peaks let mut peak_idx = forest_to_root_index(self.forest); // match order of the update data while applying it self.peaks.reverse(); // set to true when the data is needed for authentication paths updates let mut track = self.track_latest; self.track_latest = false; let mut peak_count = 0; let mut target = 1 << merges.trailing_zeros(); let mut new = delta.data[0]; update_count += 1; while target < largest { // check if either the left or right subtrees have saved for authentication paths. // If so, turn tracking on to update those paths. if target != 1 && !track { track = self.is_tracked_node(&peak_idx); } // update data only contains the nodes from the right subtrees, left nodes are // either previously known peaks or computed values let (left, right) = if target & merges != 0 { let peak = self.peaks[peak_count]; let sibling_idx = peak_idx.sibling(); // if the sibling peak is tracked, add this peaks to the set of // authentication nodes if self.is_tracked_node(&sibling_idx) { self.nodes.insert(peak_idx, new); inserted_nodes.push((peak_idx, new)); } peak_count += 1; (peak, new) } else { let update = delta.data[update_count]; update_count += 1; (new, update) }; if track { let sibling_idx = peak_idx.sibling(); if peak_idx.is_left_child() { self.nodes.insert(sibling_idx, right); inserted_nodes.push((sibling_idx, right)); } else { self.nodes.insert(sibling_idx, left); inserted_nodes.push((sibling_idx, left)); } } peak_idx = peak_idx.parent(); new = Rpo256::merge(&[left, right]); target <<= 1; } debug_assert!(peak_count == (merges.count_ones() as usize)); // restore the peaks order self.peaks.reverse(); // remove the merged peaks self.peaks.truncate(self.peaks.len() - peak_count); // add the newly computed peak, the result of the merges self.peaks.push(new); } // The rest of the update data is composed of peaks. None of these elements can contain // tracked elements because the peaks were unknown, and it is not possible to add elements // for tacking without authenticating it to a peak. self.peaks.extend_from_slice(&delta.data[update_count..]); self.forest = delta.forest; debug_assert!(self.peaks.len() == (self.forest.count_ones() as usize)); Ok(inserted_nodes) } // HELPER METHODS // -------------------------------------------------------------------------------------------- /// Returns true if this [PartialMmr] tracks authentication path for the node at the specified /// index. fn is_tracked_node(&self, node_index: &InOrderIndex) -> bool { if node_index.is_leaf() { self.nodes.contains_key(&node_index.sibling()) } else { let left_child = node_index.left_child(); let right_child = node_index.right_child(); self.nodes.contains_key(&left_child) | self.nodes.contains_key(&right_child) } } } // CONVERSIONS // ================================================================================================ impl From for PartialMmr { fn from(peaks: MmrPeaks) -> Self { Self::from_peaks(peaks) } } impl From for MmrPeaks { fn from(partial_mmr: PartialMmr) -> Self { // Safety: the [PartialMmr] maintains the constraints the number of true bits in the forest // matches the number of peaks, as required by the [MmrPeaks] MmrPeaks::new(partial_mmr.forest, partial_mmr.peaks).unwrap() } } impl From<&MmrPeaks> for PartialMmr { fn from(peaks: &MmrPeaks) -> Self { Self::from_peaks(peaks.clone()) } } impl From<&PartialMmr> for MmrPeaks { fn from(partial_mmr: &PartialMmr) -> Self { // Safety: the [PartialMmr] maintains the constraints the number of true bits in the forest // matches the number of peaks, as required by the [MmrPeaks] MmrPeaks::new(partial_mmr.forest, partial_mmr.peaks.clone()).unwrap() } } // ITERATORS // ================================================================================================ /// An iterator over every inner node of the [PartialMmr]. pub struct InnerNodeIterator<'a, I: Iterator> { nodes: &'a NodeMap, leaves: I, stack: Vec<(InOrderIndex, RpoDigest)>, seen_nodes: BTreeSet, } impl<'a, I: Iterator> Iterator for InnerNodeIterator<'a, I> { type Item = InnerNodeInfo; fn next(&mut self) -> Option { while let Some((idx, node)) = self.stack.pop() { let parent_idx = idx.parent(); let new_node = self.seen_nodes.insert(parent_idx); // if we haven't seen this node's parent before, and the node has a sibling, return // the inner node defined by the parent of this node, and move up the branch if new_node { if let Some(sibling) = self.nodes.get(&idx.sibling()) { let (left, right) = if parent_idx.left_child() == idx { (node, *sibling) } else { (*sibling, node) }; let parent = Rpo256::merge(&[left, right]); let inner_node = InnerNodeInfo { value: parent, left, right }; self.stack.push((parent_idx, parent)); return Some(inner_node); } } // the previous leaf has been processed, try to process the next leaf if let Some((pos, leaf)) = self.leaves.next() { let idx = InOrderIndex::from_leaf_pos(pos); self.stack.push((idx, leaf)); } } None } } impl Serializable for PartialMmr { fn write_into(&self, target: &mut W) { self.forest.write_into(target); self.peaks.write_into(target); self.nodes.write_into(target); target.write_bool(self.track_latest); } } impl Deserializable for PartialMmr { fn read_from( source: &mut R, ) -> Result { let forest = usize::read_from(source)?; let peaks = Vec::::read_from(source)?; let nodes = NodeMap::read_from(source)?; let track_latest = source.read_bool()?; Ok(Self { forest, peaks, nodes, track_latest }) } } // UTILS // ================================================================================================ /// Given the description of a `forest`, returns the index of the root element of the smallest tree /// in it. fn forest_to_root_index(forest: usize) -> InOrderIndex { // Count total size of all trees in the forest. let nodes = nodes_in_forest(forest); // Add the count for the parent nodes that separate each tree. These are allocated but // currently empty, and correspond to the nodes that will be used once the trees are merged. let open_trees = (forest.count_ones() - 1) as usize; // Remove the count of the right subtree of the target tree, target tree root index comes // before the subtree for the in-order tree walk. let right_subtree_count = ((1u32 << forest.trailing_zeros()) - 1) as usize; let idx = nodes + open_trees - right_subtree_count; InOrderIndex::new(idx.try_into().unwrap()) } /// Given the description of a `forest`, returns the index of the right most element. fn forest_to_rightmost_index(forest: usize) -> InOrderIndex { // Count total size of all trees in the forest. let nodes = nodes_in_forest(forest); // Add the count for the parent nodes that separate each tree. These are allocated but // currently empty, and correspond to the nodes that will be used once the trees are merged. let open_trees = (forest.count_ones() - 1) as usize; let idx = nodes + open_trees; InOrderIndex::new(idx.try_into().unwrap()) } // TESTS // ================================================================================================ #[cfg(test)] mod tests { use alloc::{collections::BTreeSet, vec::Vec}; use winter_utils::{Deserializable, Serializable}; use super::{ forest_to_rightmost_index, forest_to_root_index, InOrderIndex, MmrPeaks, PartialMmr, RpoDigest, }; use crate::merkle::{int_to_node, MerkleStore, Mmr, NodeIndex}; const LEAVES: [RpoDigest; 7] = [ int_to_node(0), int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4), int_to_node(5), int_to_node(6), ]; #[test] fn test_forest_to_root_index() { fn idx(pos: usize) -> InOrderIndex { InOrderIndex::new(pos.try_into().unwrap()) } // When there is a single tree in the forest, the index is equivalent to the number of // leaves in that tree, which is `2^n`. assert_eq!(forest_to_root_index(0b0001), idx(1)); assert_eq!(forest_to_root_index(0b0010), idx(2)); assert_eq!(forest_to_root_index(0b0100), idx(4)); assert_eq!(forest_to_root_index(0b1000), idx(8)); assert_eq!(forest_to_root_index(0b0011), idx(5)); assert_eq!(forest_to_root_index(0b0101), idx(9)); assert_eq!(forest_to_root_index(0b1001), idx(17)); assert_eq!(forest_to_root_index(0b0111), idx(13)); assert_eq!(forest_to_root_index(0b1011), idx(21)); assert_eq!(forest_to_root_index(0b1111), idx(29)); assert_eq!(forest_to_root_index(0b0110), idx(10)); assert_eq!(forest_to_root_index(0b1010), idx(18)); assert_eq!(forest_to_root_index(0b1100), idx(20)); assert_eq!(forest_to_root_index(0b1110), idx(26)); } #[test] fn test_forest_to_rightmost_index() { fn idx(pos: usize) -> InOrderIndex { InOrderIndex::new(pos.try_into().unwrap()) } for forest in 1..256 { assert!(forest_to_rightmost_index(forest).inner() % 2 == 1, "Leaves are always odd"); } assert_eq!(forest_to_rightmost_index(0b0001), idx(1)); assert_eq!(forest_to_rightmost_index(0b0010), idx(3)); assert_eq!(forest_to_rightmost_index(0b0011), idx(5)); assert_eq!(forest_to_rightmost_index(0b0100), idx(7)); assert_eq!(forest_to_rightmost_index(0b0101), idx(9)); assert_eq!(forest_to_rightmost_index(0b0110), idx(11)); assert_eq!(forest_to_rightmost_index(0b0111), idx(13)); assert_eq!(forest_to_rightmost_index(0b1000), idx(15)); assert_eq!(forest_to_rightmost_index(0b1001), idx(17)); assert_eq!(forest_to_rightmost_index(0b1010), idx(19)); assert_eq!(forest_to_rightmost_index(0b1011), idx(21)); assert_eq!(forest_to_rightmost_index(0b1100), idx(23)); assert_eq!(forest_to_rightmost_index(0b1101), idx(25)); assert_eq!(forest_to_rightmost_index(0b1110), idx(27)); assert_eq!(forest_to_rightmost_index(0b1111), idx(29)); } #[test] fn test_partial_mmr_apply_delta() { // build an MMR with 10 nodes (2 peaks) and a partial MMR based on it let mut mmr = Mmr::default(); (0..10).for_each(|i| mmr.add(int_to_node(i))); let mut partial_mmr: PartialMmr = mmr.peaks().into(); // add authentication path for position 1 and 8 { let node = mmr.get(1).unwrap(); let proof = mmr.open(1).unwrap(); partial_mmr.track(1, node, &proof.merkle_path).unwrap(); } { let node = mmr.get(8).unwrap(); let proof = mmr.open(8).unwrap(); partial_mmr.track(8, node, &proof.merkle_path).unwrap(); } // add 2 more nodes into the MMR and validate apply_delta() (10..12).for_each(|i| mmr.add(int_to_node(i))); validate_apply_delta(&mmr, &mut partial_mmr); // add 1 more node to the MMR, validate apply_delta() and start tracking the node mmr.add(int_to_node(12)); validate_apply_delta(&mmr, &mut partial_mmr); { let node = mmr.get(12).unwrap(); let proof = mmr.open(12).unwrap(); partial_mmr.track(12, node, &proof.merkle_path).unwrap(); assert!(partial_mmr.track_latest); } // by this point we are tracking authentication paths for positions: 1, 8, and 12 // add 3 more nodes to the MMR (collapses to 1 peak) and validate apply_delta() (13..16).for_each(|i| mmr.add(int_to_node(i))); validate_apply_delta(&mmr, &mut partial_mmr); } fn validate_apply_delta(mmr: &Mmr, partial: &mut PartialMmr) { let tracked_leaves = partial .nodes .iter() .filter_map(|(index, _)| if index.is_leaf() { Some(index.sibling()) } else { None }) .collect::>(); let nodes_before = partial.nodes.clone(); // compute and apply delta let delta = mmr.get_delta(partial.forest(), mmr.forest()).unwrap(); let nodes_delta = partial.apply(delta).unwrap(); // new peaks were computed correctly assert_eq!(mmr.peaks(), partial.peaks()); let mut expected_nodes = nodes_before; for (key, value) in nodes_delta { // nodes should not be duplicated assert!(expected_nodes.insert(key, value).is_none()); } // new nodes should be a combination of original nodes and delta assert_eq!(expected_nodes, partial.nodes); // make sure tracked leaves open to the same proofs as in the underlying MMR for index in tracked_leaves { let index_value: u64 = index.into(); let pos = index_value / 2; let proof1 = partial.open(pos as usize).unwrap().unwrap(); let proof2 = mmr.open(pos as usize).unwrap(); assert_eq!(proof1, proof2); } } #[test] fn test_partial_mmr_inner_nodes_iterator() { // build the MMR let mmr: Mmr = LEAVES.into(); let first_peak = mmr.peaks().peaks()[0]; // -- test single tree ---------------------------- // get path and node for position 1 let node1 = mmr.get(1).unwrap(); let proof1 = mmr.open(1).unwrap(); // create partial MMR and add authentication path to node at position 1 let mut partial_mmr: PartialMmr = mmr.peaks().into(); partial_mmr.track(1, node1, &proof1.merkle_path).unwrap(); // empty iterator should have no nodes assert_eq!(partial_mmr.inner_nodes([].iter().cloned()).next(), None); // build Merkle store from authentication paths in partial MMR let mut store: MerkleStore = MerkleStore::new(); store.extend(partial_mmr.inner_nodes([(1, node1)].iter().cloned())); let index1 = NodeIndex::new(2, 1).unwrap(); let path1 = store.get_path(first_peak, index1).unwrap().path; assert_eq!(path1, proof1.merkle_path); // -- test no duplicates -------------------------- // build the partial MMR let mut partial_mmr: PartialMmr = mmr.peaks().into(); let node0 = mmr.get(0).unwrap(); let proof0 = mmr.open(0).unwrap(); let node2 = mmr.get(2).unwrap(); let proof2 = mmr.open(2).unwrap(); partial_mmr.track(0, node0, &proof0.merkle_path).unwrap(); partial_mmr.track(1, node1, &proof1.merkle_path).unwrap(); partial_mmr.track(2, node2, &proof2.merkle_path).unwrap(); // make sure there are no duplicates let leaves = [(0, node0), (1, node1), (2, node2)]; let mut nodes = BTreeSet::new(); for node in partial_mmr.inner_nodes(leaves.iter().cloned()) { assert!(nodes.insert(node.value)); } // and also that the store is still be built correctly store.extend(partial_mmr.inner_nodes(leaves.iter().cloned())); let index0 = NodeIndex::new(2, 0).unwrap(); let index1 = NodeIndex::new(2, 1).unwrap(); let index2 = NodeIndex::new(2, 2).unwrap(); let path0 = store.get_path(first_peak, index0).unwrap().path; let path1 = store.get_path(first_peak, index1).unwrap().path; let path2 = store.get_path(first_peak, index2).unwrap().path; assert_eq!(path0, proof0.merkle_path); assert_eq!(path1, proof1.merkle_path); assert_eq!(path2, proof2.merkle_path); // -- test multiple trees ------------------------- // build the partial MMR let mut partial_mmr: PartialMmr = mmr.peaks().into(); let node5 = mmr.get(5).unwrap(); let proof5 = mmr.open(5).unwrap(); partial_mmr.track(1, node1, &proof1.merkle_path).unwrap(); partial_mmr.track(5, node5, &proof5.merkle_path).unwrap(); // build Merkle store from authentication paths in partial MMR let mut store: MerkleStore = MerkleStore::new(); store.extend(partial_mmr.inner_nodes([(1, node1), (5, node5)].iter().cloned())); let index1 = NodeIndex::new(2, 1).unwrap(); let index5 = NodeIndex::new(1, 1).unwrap(); let second_peak = mmr.peaks().peaks()[1]; let path1 = store.get_path(first_peak, index1).unwrap().path; let path5 = store.get_path(second_peak, index5).unwrap().path; assert_eq!(path1, proof1.merkle_path); assert_eq!(path5, proof5.merkle_path); } #[test] fn test_partial_mmr_add_without_track() { let mut mmr = Mmr::default(); let empty_peaks = MmrPeaks::new(0, vec![]).unwrap(); let mut partial_mmr = PartialMmr::from_peaks(empty_peaks); for el in (0..256).map(int_to_node) { mmr.add(el); partial_mmr.add(el, false); assert_eq!(mmr.peaks(), partial_mmr.peaks()); assert_eq!(mmr.forest(), partial_mmr.forest()); } } #[test] fn test_partial_mmr_add_with_track() { let mut mmr = Mmr::default(); let empty_peaks = MmrPeaks::new(0, vec![]).unwrap(); let mut partial_mmr = PartialMmr::from_peaks(empty_peaks); for i in 0..256 { let el = int_to_node(i); mmr.add(el); partial_mmr.add(el, true); assert_eq!(mmr.peaks(), partial_mmr.peaks()); assert_eq!(mmr.forest(), partial_mmr.forest()); for pos in 0..i { let mmr_proof = mmr.open(pos as usize).unwrap(); let partialmmr_proof = partial_mmr.open(pos as usize).unwrap().unwrap(); assert_eq!(mmr_proof, partialmmr_proof); } } } #[test] fn test_partial_mmr_add_existing_track() { let mut mmr = Mmr::from((0..7).map(int_to_node)); // derive a partial Mmr from it which tracks authentication path to leaf 5 let mut partial_mmr = PartialMmr::from_peaks(mmr.peaks()); let path_to_5 = mmr.open(5).unwrap().merkle_path; let leaf_at_5 = mmr.get(5).unwrap(); partial_mmr.track(5, leaf_at_5, &path_to_5).unwrap(); // add a new leaf to both Mmr and partial Mmr let leaf_at_7 = int_to_node(7); mmr.add(leaf_at_7); partial_mmr.add(leaf_at_7, false); // the openings should be the same assert_eq!(mmr.open(5).unwrap(), partial_mmr.open(5).unwrap().unwrap()); } #[test] fn test_partial_mmr_serialization() { let mmr = Mmr::from((0..7).map(int_to_node)); let forest_size = mmr.forest(); let partial_mmr = PartialMmr::from_peaks(mmr.peaks(forest_size).unwrap()); let bytes = partial_mmr.to_bytes(); let decoded = PartialMmr::read_from_bytes(&bytes).unwrap(); assert_eq!(partial_mmr, decoded); } }