Skip to main content

miden_crypto/merkle/mmr/
partial.rs

1use alloc::{
2    collections::{BTreeMap, BTreeSet},
3    vec::Vec,
4};
5
6use winter_utils::{Deserializable, Serializable};
7
8use super::{MmrDelta, MmrProof};
9use crate::{
10    Word,
11    merkle::{
12        InnerNodeInfo, MerklePath, Rpo256,
13        mmr::{InOrderIndex, MmrError, MmrPeaks, forest::Forest},
14    },
15};
16
17// TYPE ALIASES
18// ================================================================================================
19
20type NodeMap = BTreeMap<InOrderIndex, Word>;
21
22// PARTIAL MERKLE MOUNTAIN RANGE
23// ================================================================================================
24/// Partially materialized Merkle Mountain Range (MMR), used to efficiently store and update the
25/// authentication paths for a subset of the elements in a full MMR.
26///
27/// This structure store only the authentication path for a value, the value itself is stored
28/// separately.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct PartialMmr {
31    /// The version of the MMR.
32    ///
33    /// This value serves the following purposes:
34    ///
35    /// - The forest is a counter for the total number of elements in the MMR.
36    /// - Since the MMR is an append-only structure, every change to it causes a change to the
37    ///   `forest`, so this value has a dual purpose as a version tag.
38    /// - The bits in the forest also corresponds to the count and size of every perfect binary
39    ///   tree that composes the MMR structure, which server to compute indexes and perform
40    ///   validation.
41    pub(crate) forest: Forest,
42
43    /// The MMR peaks.
44    ///
45    /// The peaks are used for two reasons:
46    ///
47    /// 1. It authenticates the addition of an element to the [PartialMmr], ensuring only valid
48    ///    elements are tracked.
49    /// 2. During a MMR update peaks can be merged by hashing the left and right hand sides. The
50    ///    peaks are used as the left hand.
51    ///
52    /// All the peaks of every tree in the MMR forest. The peaks are always ordered by number of
53    /// leaves, starting from the peak with most children, to the one with least.
54    pub(crate) peaks: Vec<Word>,
55
56    /// Authentication nodes used to construct merkle paths for a subset of the MMR's leaves.
57    ///
58    /// This does not include the MMR's peaks nor the tracked nodes, only the elements required to
59    /// construct their authentication paths. This property is used to detect when elements can be
60    /// safely removed, because they are no longer required to authenticate any element in the
61    /// [PartialMmr].
62    ///
63    /// The elements in the MMR are referenced using a in-order tree index. This indexing scheme
64    /// permits for easy computation of the relative nodes (left/right children, sibling, parent),
65    /// which is useful for traversal. The indexing is also stable, meaning that merges to the
66    /// trees in the MMR can be represented without rewrites of the indexes.
67    pub(crate) nodes: NodeMap,
68
69    /// Flag indicating if the odd element should be tracked.
70    ///
71    /// This flag is necessary because the sibling of the odd doesn't exist yet, so it can not be
72    /// added into `nodes` to signal the value is being tracked.
73    pub(crate) track_latest: bool,
74}
75
76impl Default for PartialMmr {
77    /// Creates a new [PartialMmr] with default values.
78    fn default() -> Self {
79        let forest = Forest::empty();
80        let peaks = Vec::new();
81        let nodes = BTreeMap::new();
82        let track_latest = false;
83
84        Self { forest, peaks, nodes, track_latest }
85    }
86}
87
88impl PartialMmr {
89    // CONSTRUCTORS
90    // --------------------------------------------------------------------------------------------
91
92    /// Returns a new [PartialMmr] instantiated from the specified peaks.
93    pub fn from_peaks(peaks: MmrPeaks) -> Self {
94        let forest = peaks.forest();
95        let peaks = peaks.into();
96        let nodes = BTreeMap::new();
97        let track_latest = false;
98
99        Self { forest, peaks, nodes, track_latest }
100    }
101
102    /// Returns a new [PartialMmr] instantiated from the specified components.
103    ///
104    /// This constructor does not check the consistency between peaks and nodes. If the specified
105    /// peaks are nodes are inconsistent, the returned partial MMR may exhibit undefined behavior.
106    pub fn from_parts(peaks: MmrPeaks, nodes: NodeMap, track_latest: bool) -> Self {
107        let forest = peaks.forest();
108        let peaks = peaks.into();
109
110        Self { forest, peaks, nodes, track_latest }
111    }
112
113    // PUBLIC ACCESSORS
114    // --------------------------------------------------------------------------------------------
115
116    /// Returns the current `forest` of this [PartialMmr].
117    ///
118    /// This value corresponds to the version of the [PartialMmr] and the number of leaves in the
119    /// underlying MMR.
120    pub fn forest(&self) -> Forest {
121        self.forest
122    }
123
124    /// Returns the number of leaves in the underlying MMR for this [PartialMmr].
125    pub fn num_leaves(&self) -> usize {
126        self.forest.num_leaves()
127    }
128
129    /// Returns the peaks of the MMR for this [PartialMmr].
130    pub fn peaks(&self) -> MmrPeaks {
131        // expect() is OK here because the constructor ensures that MMR peaks can be constructed
132        // correctly
133        MmrPeaks::new(self.forest, self.peaks.clone()).expect("invalid MMR peaks")
134    }
135
136    /// Returns true if this partial MMR tracks an authentication path for the leaf at the
137    /// specified position.
138    pub fn is_tracked(&self, pos: usize) -> bool {
139        let leaves = self.forest.num_leaves();
140        if pos >= leaves {
141            return false;
142        } else if pos == leaves - 1 && self.forest.has_single_leaf_tree() {
143            // if the number of leaves in the MMR is odd and the position is for the last leaf
144            // whether the leaf is tracked is defined by the `track_latest` flag
145            return self.track_latest;
146        }
147
148        let leaf_index = InOrderIndex::from_leaf_pos(pos);
149        self.is_tracked_node(&leaf_index)
150    }
151
152    /// Given a leaf position, returns the Merkle path to its corresponding peak, or None if this
153    /// partial MMR does not track an authentication paths for the specified leaf.
154    ///
155    /// Note: The leaf position is the 0-indexed number corresponding to the order the leaves were
156    /// added, this corresponds to the MMR size _prior_ to adding the element. So the 1st element
157    /// has position 0, the second position 1, and so on.
158    ///
159    /// # Errors
160    /// Returns an error if the specified position is greater-or-equal than the number of leaves
161    /// in the underlying MMR.
162    pub fn open(&self, pos: usize) -> Result<Option<MmrProof>, MmrError> {
163        let tree_bit = self
164            .forest
165            .leaf_to_corresponding_tree(pos)
166            .ok_or(MmrError::PositionNotFound(pos))?;
167        let depth = tree_bit as usize;
168
169        let mut nodes = Vec::with_capacity(depth);
170        let mut idx = InOrderIndex::from_leaf_pos(pos);
171
172        while let Some(node) = self.nodes.get(&idx.sibling()) {
173            nodes.push(*node);
174            idx = idx.parent();
175        }
176
177        // If there are nodes then the path must be complete, otherwise it is a bug
178        debug_assert!(nodes.is_empty() || nodes.len() == depth);
179
180        if nodes.len() != depth {
181            // The requested `pos` is not being tracked.
182            Ok(None)
183        } else {
184            Ok(Some(MmrProof {
185                forest: self.forest,
186                position: pos,
187                merkle_path: MerklePath::new(nodes),
188            }))
189        }
190    }
191
192    // ITERATORS
193    // --------------------------------------------------------------------------------------------
194
195    /// Returns an iterator nodes of all authentication paths of this [PartialMmr].
196    pub fn nodes(&self) -> impl Iterator<Item = (&InOrderIndex, &Word)> {
197        self.nodes.iter()
198    }
199
200    /// Returns an iterator over inner nodes of this [PartialMmr] for the specified leaves.
201    ///
202    /// The order of iteration is not defined. If a leaf is not presented in this partial MMR it
203    /// is silently ignored.
204    pub fn inner_nodes<'a, I: Iterator<Item = (usize, Word)> + 'a>(
205        &'a self,
206        mut leaves: I,
207    ) -> impl Iterator<Item = InnerNodeInfo> + 'a {
208        let stack = if let Some((pos, leaf)) = leaves.next() {
209            let idx = InOrderIndex::from_leaf_pos(pos);
210            vec![(idx, leaf)]
211        } else {
212            Vec::new()
213        };
214
215        InnerNodeIterator {
216            nodes: &self.nodes,
217            leaves,
218            stack,
219            seen_nodes: BTreeSet::new(),
220        }
221    }
222
223    // STATE MUTATORS
224    // --------------------------------------------------------------------------------------------
225
226    /// Adds a new peak and optionally track it. Returns a vector of the authentication nodes
227    /// inserted into this [PartialMmr] as a result of this operation.
228    ///
229    /// When `track` is `true` the new leaf is tracked.
230    pub fn add(&mut self, leaf: Word, track: bool) -> Vec<(InOrderIndex, Word)> {
231        self.forest.append_leaf();
232        // We just incremented the forest, so this cannot panic.
233        let merges = self.forest.smallest_tree_height_unchecked();
234        let mut new_nodes = Vec::with_capacity(merges);
235
236        let peak = if merges == 0 {
237            self.track_latest = track;
238            leaf
239        } else {
240            let mut track_right = track;
241            let mut track_left = self.track_latest;
242            // After merges the forest no longer has a single-leaf tree, so
243            // `track_latest` must be cleared. The old value is already captured
244            // in `track_left` above.
245            self.track_latest = false;
246
247            let mut right = leaf;
248            let mut right_idx = self.forest.rightmost_in_order_index();
249
250            for _ in 0..merges {
251                let left = self.peaks.pop().expect("Missing peak");
252                let left_idx = right_idx.sibling();
253
254                if track_right {
255                    let old = self.nodes.insert(left_idx, left);
256                    new_nodes.push((left_idx, left));
257
258                    debug_assert!(
259                        old.is_none(),
260                        "Idx {left_idx:?} already contained an element {old:?}",
261                    );
262                };
263                if track_left {
264                    let old = self.nodes.insert(right_idx, right);
265                    new_nodes.push((right_idx, right));
266
267                    debug_assert!(
268                        old.is_none(),
269                        "Idx {right_idx:?} already contained an element {old:?}",
270                    );
271                };
272
273                // Update state for the next iteration.
274                // --------------------------------------------------------------------------------
275
276                // This layer is merged, go up one layer.
277                right_idx = right_idx.parent();
278
279                // Merge the current layer. The result is either the right element of the next
280                // merge, or a new peak.
281                right = Rpo256::merge(&[left, right]);
282
283                // This iteration merged the left and right nodes, the new value is always used as
284                // the next iteration's right node. Therefore the tracking flags of this iteration
285                // have to be merged into the right side only.
286                track_right = track_right || track_left;
287
288                // On the next iteration, a peak will be merged. If any of its children are tracked,
289                // then we have to track the left side
290                track_left = self.is_tracked_node(&right_idx.sibling());
291            }
292            right
293        };
294
295        self.peaks.push(peak);
296
297        new_nodes
298    }
299
300    /// Adds the authentication path represented by [MerklePath] if it is valid.
301    ///
302    /// The `leaf_pos` refers to the global position of the leaf in the MMR, these are 0-indexed
303    /// values assigned in a strictly monotonic fashion as elements are inserted into the MMR,
304    /// this value corresponds to the values used in the MMR structure.
305    ///
306    /// The `leaf` corresponds to the value at `leaf_pos`, and `path` is the authentication path for
307    /// that element up to its corresponding Mmr peak. The `leaf` is only used to compute the root
308    /// from the authentication path to valid the data, only the authentication data is saved in
309    /// the structure. If the value is required it should be stored out-of-band.
310    pub fn track(
311        &mut self,
312        leaf_pos: usize,
313        leaf: Word,
314        path: &MerklePath,
315    ) -> Result<(), MmrError> {
316        // Checks there is a tree with same depth as the authentication path, if not the path is
317        // invalid.
318        let tree = Forest::new(1 << path.depth());
319        if (tree & self.forest).is_empty() {
320            return Err(MmrError::UnknownPeak(path.depth()));
321        };
322
323        if leaf_pos + 1 == self.forest.num_leaves()
324            && path.depth() == 0
325            && self.peaks.last().is_some_and(|v| *v == leaf)
326        {
327            self.track_latest = true;
328            return Ok(());
329        }
330
331        // ignore the trees smaller than the target (these elements are position after the current
332        // target and don't affect the target leaf_pos)
333        let target_forest = self.forest ^ (self.forest & tree.all_smaller_trees_unchecked());
334        let peak_pos = target_forest.num_trees() - 1;
335
336        // translate from mmr leaf_pos to merkle path
337        let path_idx = leaf_pos - (target_forest ^ tree).num_leaves();
338
339        // Compute the root of the authentication path, and check it matches the current version of
340        // the PartialMmr.
341        let computed = path
342            .compute_root(path_idx as u64, leaf)
343            .map_err(MmrError::MerkleRootComputationFailed)?;
344        if self.peaks[peak_pos] != computed {
345            return Err(MmrError::PeakPathMismatch);
346        }
347
348        let mut idx = InOrderIndex::from_leaf_pos(leaf_pos);
349        for leaf in path.nodes() {
350            self.nodes.insert(idx.sibling(), *leaf);
351            idx = idx.parent();
352        }
353
354        Ok(())
355    }
356
357    /// Removes a leaf of the [PartialMmr] and the unused nodes from the authentication path.
358    ///
359    /// Note: `leaf_pos` corresponds to the position in the MMR and not on an individual tree.
360    pub fn untrack(&mut self, leaf_pos: usize) {
361        let mut idx = InOrderIndex::from_leaf_pos(leaf_pos);
362
363        // `idx` represent the element that can be computed by the authentication path, because
364        // these elements can be computed they are not saved for the authentication of the current
365        // target. In other words, if the idx is present it was added for the authentication of
366        // another element, and no more elements should be removed otherwise it would remove that
367        // element's authentication data.
368        while self.nodes.remove(&idx.sibling()).is_some() && !self.nodes.contains_key(&idx) {
369            idx = idx.parent();
370        }
371    }
372
373    /// Applies updates to this [PartialMmr] and returns a vector of new authentication nodes
374    /// inserted into the partial MMR.
375    pub fn apply(&mut self, delta: MmrDelta) -> Result<Vec<(InOrderIndex, Word)>, MmrError> {
376        if delta.forest < self.forest {
377            return Err(MmrError::InvalidPeaks(format!(
378                "forest of mmr delta {} is less than current forest {}",
379                delta.forest, self.forest
380            )));
381        }
382
383        let mut inserted_nodes = Vec::new();
384
385        if delta.forest == self.forest {
386            if !delta.data.is_empty() {
387                return Err(MmrError::InvalidUpdate);
388            }
389
390            return Ok(inserted_nodes);
391        }
392
393        // find the tree merges
394        let changes = self.forest ^ delta.forest;
395        // `largest_tree_unchecked()` panics if `changes` is empty. `changes` cannot be empty
396        // unless `self.forest == delta.forest`, which is guarded against above.
397        let largest = changes.largest_tree_unchecked();
398        // The largest tree itself also cannot be an empty forest, so this cannot panic either.
399        let merges = self.forest & largest.all_smaller_trees_unchecked();
400
401        debug_assert!(
402            !self.track_latest || merges.has_single_leaf_tree(),
403            "if there is an odd element, a merge is required"
404        );
405
406        // count the number elements needed to produce largest from the current state
407        let (merge_count, new_peaks) = if !merges.is_empty() {
408            let depth = largest.smallest_tree_height_unchecked();
409            // `merges` also cannot be an empty forest, so this cannot panic either.
410            let skipped = merges.smallest_tree_height_unchecked();
411            let computed = merges.num_trees() - 1;
412            let merge_count = depth - skipped - computed;
413
414            let new_peaks = delta.forest & largest.all_smaller_trees_unchecked();
415
416            (merge_count, new_peaks)
417        } else {
418            (0, changes)
419        };
420
421        // verify the delta size
422        if delta.data.len() != merge_count + new_peaks.num_trees() {
423            return Err(MmrError::InvalidUpdate);
424        }
425
426        // keeps track of how many data elements from the update have been consumed
427        let mut update_count = 0;
428
429        if !merges.is_empty() {
430            // starts at the smallest peak and follows the merged peaks
431            let mut peak_idx = self.forest.root_in_order_index();
432
433            // match order of the update data while applying it
434            self.peaks.reverse();
435
436            // set to true when the data is needed for authentication paths updates
437            let mut track = self.track_latest;
438            self.track_latest = false;
439
440            let mut peak_count = 0;
441            let mut target = merges.smallest_tree_unchecked();
442            let mut new = delta.data[0];
443            update_count += 1;
444
445            while target < largest {
446                // check if either the left or right subtrees have saved for authentication paths.
447                // If so, turn tracking on to update those paths.
448                if target != Forest::new(1) && !track {
449                    track = self.is_tracked_node(&peak_idx);
450                }
451
452                // update data only contains the nodes from the right subtrees, left nodes are
453                // either previously known peaks or computed values
454                let (left, right) = if !(target & merges).is_empty() {
455                    let peak = self.peaks[peak_count];
456                    let sibling_idx = peak_idx.sibling();
457
458                    // if the sibling peak is tracked, add this peaks to the set of
459                    // authentication nodes
460                    if self.is_tracked_node(&sibling_idx) {
461                        self.nodes.insert(peak_idx, new);
462                        inserted_nodes.push((peak_idx, new));
463                    }
464                    peak_count += 1;
465                    (peak, new)
466                } else {
467                    let update = delta.data[update_count];
468                    update_count += 1;
469                    (new, update)
470                };
471
472                if track {
473                    let sibling_idx = peak_idx.sibling();
474                    if peak_idx.is_left_child() {
475                        self.nodes.insert(sibling_idx, right);
476                        inserted_nodes.push((sibling_idx, right));
477                    } else {
478                        self.nodes.insert(sibling_idx, left);
479                        inserted_nodes.push((sibling_idx, left));
480                    }
481                }
482
483                peak_idx = peak_idx.parent();
484                new = Rpo256::merge(&[left, right]);
485                target = target.next_larger_tree();
486            }
487
488            debug_assert!(peak_count == merges.num_trees());
489
490            // restore the peaks order
491            self.peaks.reverse();
492            // remove the merged peaks
493            self.peaks.truncate(self.peaks.len() - peak_count);
494            // add the newly computed peak, the result of the merges
495            self.peaks.push(new);
496        }
497
498        // The rest of the update data is composed of peaks. None of these elements can contain
499        // tracked elements because the peaks were unknown, and it is not possible to add elements
500        // for tacking without authenticating it to a peak.
501        self.peaks.extend_from_slice(&delta.data[update_count..]);
502        self.forest = delta.forest;
503
504        debug_assert!(self.peaks.len() == self.forest.num_trees());
505
506        Ok(inserted_nodes)
507    }
508
509    // HELPER METHODS
510    // --------------------------------------------------------------------------------------------
511
512    /// Returns true if this [PartialMmr] tracks authentication path for the node at the specified
513    /// index.
514    fn is_tracked_node(&self, node_index: &InOrderIndex) -> bool {
515        if node_index.is_leaf() {
516            self.nodes.contains_key(&node_index.sibling())
517        } else {
518            let left_child = node_index.left_child();
519            let right_child = node_index.right_child();
520            self.nodes.contains_key(&left_child) | self.nodes.contains_key(&right_child)
521        }
522    }
523}
524
525// CONVERSIONS
526// ================================================================================================
527
528impl From<MmrPeaks> for PartialMmr {
529    fn from(peaks: MmrPeaks) -> Self {
530        Self::from_peaks(peaks)
531    }
532}
533
534impl From<PartialMmr> for MmrPeaks {
535    fn from(partial_mmr: PartialMmr) -> Self {
536        // Safety: the [PartialMmr] maintains the constraints the number of true bits in the forest
537        // matches the number of peaks, as required by the [MmrPeaks]
538        MmrPeaks::new(partial_mmr.forest, partial_mmr.peaks).unwrap()
539    }
540}
541
542impl From<&MmrPeaks> for PartialMmr {
543    fn from(peaks: &MmrPeaks) -> Self {
544        Self::from_peaks(peaks.clone())
545    }
546}
547
548impl From<&PartialMmr> for MmrPeaks {
549    fn from(partial_mmr: &PartialMmr) -> Self {
550        // Safety: the [PartialMmr] maintains the constraints the number of true bits in the forest
551        // matches the number of peaks, as required by the [MmrPeaks]
552        MmrPeaks::new(partial_mmr.forest, partial_mmr.peaks.clone()).unwrap()
553    }
554}
555
556// ITERATORS
557// ================================================================================================
558
559/// An iterator over every inner node of the [PartialMmr].
560pub struct InnerNodeIterator<'a, I: Iterator<Item = (usize, Word)>> {
561    nodes: &'a NodeMap,
562    leaves: I,
563    stack: Vec<(InOrderIndex, Word)>,
564    seen_nodes: BTreeSet<InOrderIndex>,
565}
566
567impl<I: Iterator<Item = (usize, Word)>> Iterator for InnerNodeIterator<'_, I> {
568    type Item = InnerNodeInfo;
569
570    fn next(&mut self) -> Option<Self::Item> {
571        while let Some((idx, node)) = self.stack.pop() {
572            let parent_idx = idx.parent();
573            let new_node = self.seen_nodes.insert(parent_idx);
574
575            // if we haven't seen this node's parent before, and the node has a sibling, return
576            // the inner node defined by the parent of this node, and move up the branch
577            if new_node && let Some(sibling) = self.nodes.get(&idx.sibling()) {
578                let (left, right) = if parent_idx.left_child() == idx {
579                    (node, *sibling)
580                } else {
581                    (*sibling, node)
582                };
583                let parent = Rpo256::merge(&[left, right]);
584                let inner_node = InnerNodeInfo { value: parent, left, right };
585
586                self.stack.push((parent_idx, parent));
587                return Some(inner_node);
588            }
589
590            // the previous leaf has been processed, try to process the next leaf
591            if let Some((pos, leaf)) = self.leaves.next() {
592                let idx = InOrderIndex::from_leaf_pos(pos);
593                self.stack.push((idx, leaf));
594            }
595        }
596
597        None
598    }
599}
600
601impl Serializable for PartialMmr {
602    fn write_into<W: winter_utils::ByteWriter>(&self, target: &mut W) {
603        self.forest.num_leaves().write_into(target);
604        self.peaks.write_into(target);
605        self.nodes.write_into(target);
606        target.write_bool(self.track_latest);
607    }
608}
609
610impl Deserializable for PartialMmr {
611    fn read_from<R: winter_utils::ByteReader>(
612        source: &mut R,
613    ) -> Result<Self, winter_utils::DeserializationError> {
614        let forest = Forest::new(usize::read_from(source)?);
615        let peaks = Vec::<Word>::read_from(source)?;
616        let nodes = NodeMap::read_from(source)?;
617        let track_latest = source.read_bool()?;
618
619        Ok(Self { forest, peaks, nodes, track_latest })
620    }
621}
622
623// TESTS
624// ================================================================================================
625
626#[cfg(test)]
627mod tests {
628    use alloc::{collections::BTreeSet, vec::Vec};
629
630    use winter_utils::{Deserializable, Serializable};
631
632    use super::{MmrPeaks, PartialMmr};
633    use crate::{
634        Word,
635        merkle::{
636            NodeIndex, int_to_node,
637            mmr::{Mmr, forest::Forest},
638            store::MerkleStore,
639        },
640    };
641
642    const LEAVES: [Word; 7] = [
643        int_to_node(0),
644        int_to_node(1),
645        int_to_node(2),
646        int_to_node(3),
647        int_to_node(4),
648        int_to_node(5),
649        int_to_node(6),
650    ];
651
652    #[test]
653    fn test_partial_mmr_apply_delta() {
654        // build an MMR with 10 nodes (2 peaks) and a partial MMR based on it
655        let mut mmr = Mmr::default();
656        (0..10).for_each(|i| mmr.add(int_to_node(i)));
657        let mut partial_mmr: PartialMmr = mmr.peaks().into();
658
659        // add authentication path for position 1 and 8
660        {
661            let node = mmr.get(1).unwrap();
662            let proof = mmr.open(1).unwrap();
663            partial_mmr.track(1, node, &proof.merkle_path).unwrap();
664        }
665
666        {
667            let node = mmr.get(8).unwrap();
668            let proof = mmr.open(8).unwrap();
669            partial_mmr.track(8, node, &proof.merkle_path).unwrap();
670        }
671
672        // add 2 more nodes into the MMR and validate apply_delta()
673        (10..12).for_each(|i| mmr.add(int_to_node(i)));
674        validate_apply_delta(&mmr, &mut partial_mmr);
675
676        // add 1 more node to the MMR, validate apply_delta() and start tracking the node
677        mmr.add(int_to_node(12));
678        validate_apply_delta(&mmr, &mut partial_mmr);
679        {
680            let node = mmr.get(12).unwrap();
681            let proof = mmr.open(12).unwrap();
682            partial_mmr.track(12, node, &proof.merkle_path).unwrap();
683            assert!(partial_mmr.track_latest);
684        }
685
686        // by this point we are tracking authentication paths for positions: 1, 8, and 12
687
688        // add 3 more nodes to the MMR (collapses to 1 peak) and validate apply_delta()
689        (13..16).for_each(|i| mmr.add(int_to_node(i)));
690        validate_apply_delta(&mmr, &mut partial_mmr);
691    }
692
693    fn validate_apply_delta(mmr: &Mmr, partial: &mut PartialMmr) {
694        let tracked_leaves = partial
695            .nodes
696            .iter()
697            .filter(|&(index, _)| index.is_leaf())
698            .map(|(index, _)| index.sibling())
699            .collect::<Vec<_>>();
700        let nodes_before = partial.nodes.clone();
701
702        // compute and apply delta
703        let delta = mmr.get_delta(partial.forest(), mmr.forest()).unwrap();
704        let nodes_delta = partial.apply(delta).unwrap();
705
706        // new peaks were computed correctly
707        assert_eq!(mmr.peaks(), partial.peaks());
708
709        let mut expected_nodes = nodes_before;
710        for (key, value) in nodes_delta {
711            // nodes should not be duplicated
712            assert!(expected_nodes.insert(key, value).is_none());
713        }
714
715        // new nodes should be a combination of original nodes and delta
716        assert_eq!(expected_nodes, partial.nodes);
717
718        // make sure tracked leaves open to the same proofs as in the underlying MMR
719        for index in tracked_leaves {
720            let pos = index.inner() / 2;
721            let proof1 = partial.open(pos).unwrap().unwrap();
722            let proof2 = mmr.open(pos).unwrap();
723            assert_eq!(proof1, proof2);
724        }
725    }
726
727    #[test]
728    fn test_partial_mmr_inner_nodes_iterator() {
729        // build the MMR
730        let mmr: Mmr = LEAVES.into();
731        let first_peak = mmr.peaks().peaks()[0];
732
733        // -- test single tree ----------------------------
734
735        // get path and node for position 1
736        let node1 = mmr.get(1).unwrap();
737        let proof1 = mmr.open(1).unwrap();
738
739        // create partial MMR and add authentication path to node at position 1
740        let mut partial_mmr: PartialMmr = mmr.peaks().into();
741        partial_mmr.track(1, node1, &proof1.merkle_path).unwrap();
742
743        // empty iterator should have no nodes
744        assert_eq!(partial_mmr.inner_nodes([].iter().cloned()).next(), None);
745
746        // build Merkle store from authentication paths in partial MMR
747        let mut store: MerkleStore = MerkleStore::new();
748        store.extend(partial_mmr.inner_nodes([(1, node1)].iter().cloned()));
749
750        let index1 = NodeIndex::new(2, 1).unwrap();
751        let path1 = store.get_path(first_peak, index1).unwrap().path;
752
753        assert_eq!(path1, proof1.merkle_path);
754
755        // -- test no duplicates --------------------------
756
757        // build the partial MMR
758        let mut partial_mmr: PartialMmr = mmr.peaks().into();
759
760        let node0 = mmr.get(0).unwrap();
761        let proof0 = mmr.open(0).unwrap();
762
763        let node2 = mmr.get(2).unwrap();
764        let proof2 = mmr.open(2).unwrap();
765
766        partial_mmr.track(0, node0, &proof0.merkle_path).unwrap();
767        partial_mmr.track(1, node1, &proof1.merkle_path).unwrap();
768        partial_mmr.track(2, node2, &proof2.merkle_path).unwrap();
769
770        // make sure there are no duplicates
771        let leaves = [(0, node0), (1, node1), (2, node2)];
772        let mut nodes = BTreeSet::new();
773        for node in partial_mmr.inner_nodes(leaves.iter().cloned()) {
774            assert!(nodes.insert(node.value));
775        }
776
777        // and also that the store is still be built correctly
778        store.extend(partial_mmr.inner_nodes(leaves.iter().cloned()));
779
780        let index0 = NodeIndex::new(2, 0).unwrap();
781        let index1 = NodeIndex::new(2, 1).unwrap();
782        let index2 = NodeIndex::new(2, 2).unwrap();
783
784        let path0 = store.get_path(first_peak, index0).unwrap().path;
785        let path1 = store.get_path(first_peak, index1).unwrap().path;
786        let path2 = store.get_path(first_peak, index2).unwrap().path;
787
788        assert_eq!(path0, proof0.merkle_path);
789        assert_eq!(path1, proof1.merkle_path);
790        assert_eq!(path2, proof2.merkle_path);
791
792        // -- test multiple trees -------------------------
793
794        // build the partial MMR
795        let mut partial_mmr: PartialMmr = mmr.peaks().into();
796
797        let node5 = mmr.get(5).unwrap();
798        let proof5 = mmr.open(5).unwrap();
799
800        partial_mmr.track(1, node1, &proof1.merkle_path).unwrap();
801        partial_mmr.track(5, node5, &proof5.merkle_path).unwrap();
802
803        // build Merkle store from authentication paths in partial MMR
804        let mut store: MerkleStore = MerkleStore::new();
805        store.extend(partial_mmr.inner_nodes([(1, node1), (5, node5)].iter().cloned()));
806
807        let index1 = NodeIndex::new(2, 1).unwrap();
808        let index5 = NodeIndex::new(1, 1).unwrap();
809
810        let second_peak = mmr.peaks().peaks()[1];
811
812        let path1 = store.get_path(first_peak, index1).unwrap().path;
813        let path5 = store.get_path(second_peak, index5).unwrap().path;
814
815        assert_eq!(path1, proof1.merkle_path);
816        assert_eq!(path5, proof5.merkle_path);
817    }
818
819    #[test]
820    fn test_partial_mmr_add_without_track() {
821        let mut mmr = Mmr::default();
822        let empty_peaks = MmrPeaks::new(Forest::empty(), vec![]).unwrap();
823        let mut partial_mmr = PartialMmr::from_peaks(empty_peaks);
824
825        for el in (0..256).map(int_to_node) {
826            mmr.add(el);
827            partial_mmr.add(el, false);
828
829            assert_eq!(mmr.peaks(), partial_mmr.peaks());
830            assert_eq!(mmr.forest(), partial_mmr.forest());
831        }
832    }
833
834    #[test]
835    fn test_partial_mmr_add_with_track() {
836        let mut mmr = Mmr::default();
837        let empty_peaks = MmrPeaks::new(Forest::empty(), vec![]).unwrap();
838        let mut partial_mmr = PartialMmr::from_peaks(empty_peaks);
839
840        for i in 0..256 {
841            let el = int_to_node(i as u64);
842            mmr.add(el);
843            partial_mmr.add(el, true);
844
845            assert_eq!(mmr.peaks(), partial_mmr.peaks());
846            assert_eq!(mmr.forest(), partial_mmr.forest());
847
848            for pos in 0..i {
849                let mmr_proof = mmr.open(pos).unwrap();
850                let partialmmr_proof = partial_mmr.open(pos).unwrap().unwrap();
851                assert_eq!(mmr_proof, partialmmr_proof);
852            }
853        }
854    }
855
856    #[test]
857    fn test_partial_mmr_add_existing_track() {
858        let mut mmr = Mmr::from((0..7).map(int_to_node));
859
860        // derive a partial Mmr from it which tracks authentication path to leaf 5
861        let mut partial_mmr = PartialMmr::from_peaks(mmr.peaks());
862        let path_to_5 = mmr.open(5).unwrap().merkle_path;
863        let leaf_at_5 = mmr.get(5).unwrap();
864        partial_mmr.track(5, leaf_at_5, &path_to_5).unwrap();
865
866        // add a new leaf to both Mmr and partial Mmr
867        let leaf_at_7 = int_to_node(7);
868        mmr.add(leaf_at_7);
869        partial_mmr.add(leaf_at_7, false);
870
871        // the openings should be the same
872        assert_eq!(mmr.open(5).unwrap(), partial_mmr.open(5).unwrap().unwrap());
873    }
874
875    #[test]
876    fn test_partial_mmr_add_clears_track_latest_after_merge() {
877        let mut mmr = Mmr::default();
878        let empty_peaks = MmrPeaks::new(Forest::empty(), vec![]).unwrap();
879        let mut partial_mmr = PartialMmr::from_peaks(empty_peaks);
880
881        let leaf_0 = int_to_node(0);
882        mmr.add(leaf_0);
883        partial_mmr.add(leaf_0, true);
884        assert!(partial_mmr.track_latest);
885
886        // This append triggers a merge and there is no longer a single-leaf tree in the forest.
887        let leaf_1 = int_to_node(1);
888        mmr.add(leaf_1);
889        partial_mmr.add(leaf_1, false);
890        assert!(!partial_mmr.track_latest);
891
892        // With stale `track_latest` this panicked in debug mode.
893        mmr.add(int_to_node(2));
894        let delta = mmr.get_delta(partial_mmr.forest(), mmr.forest()).unwrap();
895        partial_mmr.apply(delta).unwrap();
896
897        assert_eq!(mmr.peaks(), partial_mmr.peaks());
898        assert_eq!(mmr.open(0).unwrap(), partial_mmr.open(0).unwrap().unwrap());
899    }
900
901    #[test]
902    fn test_partial_mmr_serialization() {
903        let mmr = Mmr::from((0..7).map(int_to_node));
904        let partial_mmr = PartialMmr::from_peaks(mmr.peaks());
905
906        let bytes = partial_mmr.to_bytes();
907        let decoded = PartialMmr::read_from_bytes(&bytes).unwrap();
908
909        assert_eq!(partial_mmr, decoded);
910    }
911
912    #[test]
913    fn test_partial_mmr_untrack() {
914        // build the MMR
915        let mmr: Mmr = LEAVES.into();
916
917        // get path and node for position 1
918        let node1 = mmr.get(1).unwrap();
919        let proof1 = mmr.open(1).unwrap();
920
921        // get path and node for position 2
922        let node2 = mmr.get(2).unwrap();
923        let proof2 = mmr.open(2).unwrap();
924
925        // create partial MMR and add authentication path to nodes at position 1 and 2
926        let mut partial_mmr: PartialMmr = mmr.peaks().into();
927        partial_mmr.track(1, node1, &proof1.merkle_path).unwrap();
928        partial_mmr.track(2, node2, &proof2.merkle_path).unwrap();
929
930        // untrack nodes at positions 1 and 2
931        partial_mmr.untrack(1);
932        partial_mmr.untrack(2);
933
934        // nodes should not longer be tracked
935        assert!(!partial_mmr.is_tracked(1));
936        assert!(!partial_mmr.is_tracked(2));
937        assert_eq!(partial_mmr.nodes().count(), 0);
938    }
939}