miden_crypto/merkle/smt/
partial.rs

1use winter_utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
2
3use super::{LeafIndex, SMT_DEPTH};
4use crate::{
5    EMPTY_WORD, Word,
6    merkle::{
7        InnerNode, InnerNodeInfo, MerkleError, NodeIndex, Smt, SmtLeaf, SmtProof, SparseMerklePath,
8        smt::{InnerNodes, Leaves, SparseMerkleTree},
9    },
10};
11
12/// A partial version of an [`Smt`].
13///
14/// This type can track a subset of the key-value pairs of a full [`Smt`] and allows for updating
15/// those pairs to compute the new root of the tree, as if the updates had been done on the full
16/// tree. This is useful so that not all leaves have to be present and loaded into memory to compute
17/// an update.
18///
19/// To facilitate this, a partial SMT requires that the merkle paths of every key-value pair are
20/// added to the tree. This means this pair is considered "tracked" and can be updated.
21///
22/// An important caveat is that only pairs whose merkle paths were added can be updated. Attempting
23/// to update an untracked value will result in an error. See [`PartialSmt::insert`] for more
24/// details.
25///
26/// Once a partial SMT has been constructed, its root is set in stone. All subsequently added proofs
27/// or merkle paths must match that root, otherwise an error is returned.
28#[derive(Debug, Clone, PartialEq, Eq)]
29#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
30pub struct PartialSmt(Smt);
31
32impl PartialSmt {
33    // CONSTRUCTORS
34    // --------------------------------------------------------------------------------------------
35
36    /// Constructs a [`PartialSmt`] from a root.
37    ///
38    /// All subsequently added proofs or paths must have the same root.
39    pub fn new(root: Word) -> Self {
40        let mut partial_smt = Self(Smt::default());
41
42        partial_smt.0.set_root(root);
43
44        partial_smt
45    }
46
47    /// Instantiates a new [`PartialSmt`] by calling [`PartialSmt::add_proof`] for all [`SmtProof`]s
48    /// in the provided iterator.
49    ///
50    /// If the provided iterator is empty, an empty [`PartialSmt`] is returned.
51    ///
52    /// # Errors
53    ///
54    /// Returns an error if:
55    /// - the roots of the provided proofs are not the same.
56    pub fn from_proofs<I>(proofs: I) -> Result<Self, MerkleError>
57    where
58        I: IntoIterator<Item = SmtProof>,
59    {
60        let mut proofs = proofs.into_iter();
61
62        let Some(first_proof) = proofs.next() else {
63            return Ok(Self::default());
64        };
65
66        // Add the first path to an empty partial SMT without checking that the existing root
67        // matches the new one. This sets the expected root to the root of the first proof and all
68        // subsequently added proofs must match it.
69        let mut partial_smt = Self::default();
70        let (path, leaf) = first_proof.into_parts();
71        let path_root = partial_smt.add_path_unchecked(leaf, path);
72        partial_smt.0.set_root(path_root);
73
74        for proof in proofs {
75            partial_smt.add_proof(proof)?;
76        }
77
78        Ok(partial_smt)
79    }
80
81    // PUBLIC ACCESSORS
82    // --------------------------------------------------------------------------------------------
83
84    /// Returns the root of the tree.
85    pub fn root(&self) -> Word {
86        self.0.root()
87    }
88
89    /// Returns an opening of the leaf associated with `key`. Conceptually, an opening is a Merkle
90    /// path to the leaf, as well as the leaf itself.
91    ///
92    /// # Errors
93    ///
94    /// Returns an error if:
95    /// - the key is not tracked by this partial SMT.
96    pub fn open(&self, key: &Word) -> Result<SmtProof, MerkleError> {
97        if !self.is_leaf_tracked(key) {
98            return Err(MerkleError::UntrackedKey(*key));
99        }
100
101        Ok(self.0.open(key))
102    }
103
104    /// Returns the leaf to which `key` maps
105    ///
106    /// # Errors
107    ///
108    /// Returns an error if:
109    /// - the key is not tracked by this partial SMT.
110    pub fn get_leaf(&self, key: &Word) -> Result<SmtLeaf, MerkleError> {
111        if !self.is_leaf_tracked(key) {
112            return Err(MerkleError::UntrackedKey(*key));
113        }
114
115        Ok(self.0.get_leaf(key))
116    }
117
118    /// Returns the value associated with `key`.
119    ///
120    /// # Errors
121    ///
122    /// Returns an error if:
123    /// - the key is not tracked by this partial SMT.
124    pub fn get_value(&self, key: &Word) -> Result<Word, MerkleError> {
125        if !self.is_leaf_tracked(key) {
126            return Err(MerkleError::UntrackedKey(*key));
127        }
128
129        Ok(self.0.get_value(key))
130    }
131
132    // STATE MUTATORS
133    // --------------------------------------------------------------------------------------------
134
135    /// Inserts a value at the specified key, returning the previous value associated with that key.
136    /// Recall that by definition, any key that hasn't been updated is associated with
137    /// [`Smt::EMPTY_VALUE`].
138    ///
139    /// This also recomputes all hashes between the leaf (associated with the key) and the root,
140    /// updating the root itself.
141    ///
142    /// # Errors
143    ///
144    /// Returns an error if:
145    /// - the key and its merkle path were not previously added (using [`PartialSmt::add_path`]) to
146    ///   this [`PartialSmt`], which means it is almost certainly incorrect to update its value. If
147    ///   an error is returned the tree is in the same state as before.
148    /// - inserting the key-value pair would exceed [`super::MAX_LEAF_ENTRIES`] (1024 entries) in
149    ///   the leaf.
150    pub fn insert(&mut self, key: Word, value: Word) -> Result<Word, MerkleError> {
151        if !self.is_leaf_tracked(&key) {
152            return Err(MerkleError::UntrackedKey(key));
153        }
154
155        let previous_value = self.0.insert(key, value)?;
156
157        // If the value was removed the SmtLeaf was removed as well by the underlying Smt
158        // implementation. However, we still want to consider that leaf tracked so it can be
159        // read and written to, so we reinsert an empty SmtLeaf.
160        if value == EMPTY_WORD {
161            let leaf_index = Smt::key_to_leaf_index(&key);
162            self.0.leaves.insert(leaf_index.value(), SmtLeaf::Empty(leaf_index));
163        }
164
165        Ok(previous_value)
166    }
167
168    /// Adds an [`SmtProof`] to this [`PartialSmt`].
169    ///
170    /// This is a convenience method which calls [`Self::add_path`] on the proof. See its
171    /// documentation for details on errors.
172    pub fn add_proof(&mut self, proof: SmtProof) -> Result<(), MerkleError> {
173        let (path, leaf) = proof.into_parts();
174        self.add_path(leaf, path)
175    }
176
177    /// Adds a leaf and its sparse merkle path to this [`PartialSmt`].
178    ///
179    /// If this function was called, any key that is part of the `leaf` can subsequently be updated
180    /// to a new value and produce a correct new tree root.
181    ///
182    /// # Errors
183    ///
184    /// Returns an error if:
185    /// - the new root after the insertion of the leaf and the path does not match the existing
186    ///   root. If an error is returned, the tree is left in an inconsistent state.
187    pub fn add_path(&mut self, leaf: SmtLeaf, path: SparseMerklePath) -> Result<(), MerkleError> {
188        let path_root = self.add_path_unchecked(leaf, path);
189
190        // Check if the newly added merkle path is consistent with the existing tree. If not, the
191        // merkle path was invalid or computed against another tree.
192        if self.root() != path_root {
193            return Err(MerkleError::ConflictingRoots {
194                expected_root: self.root(),
195                actual_root: path_root,
196            });
197        }
198
199        Ok(())
200    }
201
202    /// Returns an iterator over the inner nodes of the [`PartialSmt`].
203    pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
204        self.0.inner_nodes()
205    }
206
207    /// Returns an iterator over the [`InnerNode`] and the respective [`NodeIndex`] of the
208    /// [`PartialSmt`].
209    pub fn inner_node_indices(&self) -> impl Iterator<Item = (NodeIndex, InnerNode)> + '_ {
210        self.0.inner_node_indices()
211    }
212
213    /// Returns an iterator over the tracked, non-empty leaves of the [`PartialSmt`] in arbitrary
214    /// order.
215    pub fn leaves(&self) -> impl Iterator<Item = (LeafIndex<SMT_DEPTH>, &SmtLeaf)> {
216        // The partial SMT also contains empty leaves, so we have to filter them out.
217        self.0.leaves().filter_map(
218            |(leaf_idx, leaf)| {
219                if leaf.is_empty() { None } else { Some((leaf_idx, leaf)) }
220            },
221        )
222    }
223
224    /// Returns an iterator over the tracked leaves of the [`PartialSmt`] in arbitrary order.
225    ///
226    /// Note that this includes empty leaves.
227    pub fn tracked_leaves(&self) -> impl Iterator<Item = (LeafIndex<SMT_DEPTH>, &SmtLeaf)> {
228        self.0.leaves()
229    }
230
231    /// Returns an iterator over the tracked, non-empty key-value pairs of the [`PartialSmt`] in
232    /// arbitrary order.
233    pub fn entries(&self) -> impl Iterator<Item = &(Word, Word)> {
234        self.0.entries()
235    }
236
237    /// Returns the number of tracked leaves in this tree, which includes empty ones.
238    ///
239    /// Note that this may return a different value from [Self::num_entries()] as a single leaf may
240    /// contain more than one key-value pair.
241    pub fn num_leaves(&self) -> usize {
242        self.0.num_leaves()
243    }
244
245    /// Returns the number of tracked, non-empty key-value pairs in this tree.
246    ///
247    /// Note that this may return a different value from [Self::num_leaves()] as a single leaf may
248    /// contain more than one key-value pair.
249    pub fn num_entries(&self) -> usize {
250        self.0.num_entries()
251    }
252
253    /// Returns a boolean value indicating whether the [`PartialSmt`] tracks any leaves.
254    ///
255    /// Note that if a partial SMT does not track leaves, its root is not necessarily the empty SMT
256    /// root, since it could have been constructed from a different root but without tracking any
257    /// leaves.
258    pub fn tracks_leaves(&self) -> bool {
259        !self.0.leaves.is_empty()
260    }
261
262    // PRIVATE HELPERS
263    // --------------------------------------------------------------------------------------------
264
265    /// Adds a leaf and its sparse merkle path to this [`PartialSmt`] and returns the root of the
266    /// inserted path.
267    ///
268    /// This does not check that the path root matches the existing root of the tree and if so, the
269    /// tree is left in an inconsistent state. This state can be made consistent again by setting
270    /// the root of the SMT to the path root.
271    fn add_path_unchecked(&mut self, leaf: SmtLeaf, path: SparseMerklePath) -> Word {
272        let mut current_index = leaf.index().index;
273
274        let mut node_hash_at_current_index = leaf.hash();
275
276        // We insert directly into the leaves for two reasons:
277        // - We can directly insert the leaf as it is without having to loop over its entries to
278        //   call Smt::perform_insert.
279        // - If the leaf is SmtLeaf::Empty, we will also insert it, which means this leaf is
280        //   considered tracked by the partial SMT as it is part of the leaves map. When calling
281        //   PartialSmt::insert, this will not error for such empty leaves whose merkle path was
282        //   added, but will error for otherwise non-existent leaves whose paths were not added,
283        //   which is what we want.
284        let prev_entries = self
285            .0
286            .leaves
287            .get(&current_index.value())
288            .map(|leaf| leaf.num_entries())
289            .unwrap_or(0);
290        let current_entries = leaf.num_entries();
291        self.0.leaves.insert(current_index.value(), leaf);
292
293        // Guaranteed not to over/underflow. All variables are <= MAX_LEAF_ENTRIES and result > 0.
294        self.0.num_entries = self.0.num_entries + current_entries - prev_entries;
295
296        for sibling_hash in path {
297            // Find the index of the sibling node and compute whether it is a left or right child.
298            let is_sibling_right = current_index.sibling().is_value_odd();
299
300            // Move the index up so it points to the parent of the current index and the sibling.
301            current_index.move_up();
302
303            // Construct the new parent node from the child that was updated and the sibling from
304            // the merkle path.
305            let new_parent_node = if is_sibling_right {
306                InnerNode {
307                    left: node_hash_at_current_index,
308                    right: sibling_hash,
309                }
310            } else {
311                InnerNode {
312                    left: sibling_hash,
313                    right: node_hash_at_current_index,
314                }
315            };
316
317            self.0.insert_inner_node(current_index, new_parent_node);
318
319            node_hash_at_current_index = self.0.get_inner_node(current_index).hash();
320        }
321
322        node_hash_at_current_index
323    }
324
325    /// Returns true if the key's merkle path was previously added to this partial SMT and can be
326    /// sensibly updated to a new value.
327    /// In particular, this returns true for keys whose value was empty **but** their merkle paths
328    /// were added, while it returns false if the merkle paths were **not** added.
329    fn is_leaf_tracked(&self, key: &Word) -> bool {
330        self.0.leaves.contains_key(&Smt::key_to_leaf_index(key).value())
331    }
332}
333
334impl Default for PartialSmt {
335    /// Returns a new, empty [`PartialSmt`].
336    ///
337    /// All leaves in the returned tree are set to [`Smt::EMPTY_VALUE`].
338    fn default() -> Self {
339        Self::new(Smt::EMPTY_ROOT)
340    }
341}
342
343// CONVERSIONS
344// ================================================================================================
345
346impl From<Smt> for PartialSmt {
347    fn from(smt: Smt) -> Self {
348        PartialSmt(smt)
349    }
350}
351
352// SERIALIZATION
353// ================================================================================================
354
355impl Serializable for PartialSmt {
356    fn write_into<W: ByteWriter>(&self, target: &mut W) {
357        target.write(self.root());
358        target.write_usize(self.0.leaves.len());
359        for (i, leaf) in &self.0.leaves {
360            target.write_u64(*i);
361            target.write(leaf);
362        }
363        target.write_usize(self.0.inner_nodes.len());
364        for (idx, node) in &self.0.inner_nodes {
365            target.write(idx);
366            target.write(node);
367        }
368    }
369}
370
371impl Deserializable for PartialSmt {
372    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
373        let root: Word = source.read()?;
374
375        let mut leaves = Leaves::default();
376        for _ in 0..source.read_usize()? {
377            let pos: u64 = source.read()?;
378            let leaf: SmtLeaf = source.read()?;
379            leaves.insert(pos, leaf);
380        }
381
382        let mut nodes = InnerNodes::default();
383        for _ in 0..source.read_usize()? {
384            let idx: NodeIndex = source.read()?;
385            let node: InnerNode = source.read()?;
386            nodes.insert(idx, node);
387        }
388
389        let smt = Smt::from_raw_parts(nodes, leaves, root);
390        Ok(PartialSmt(smt))
391    }
392}
393
394// TESTS
395// ================================================================================================
396
397#[cfg(test)]
398mod tests {
399
400    use alloc::collections::{BTreeMap, BTreeSet};
401
402    use assert_matches::assert_matches;
403    use rand_utils::{rand_array, rand_value};
404    use winter_math::fields::f64::BaseElement as Felt;
405
406    use super::*;
407    use crate::{EMPTY_WORD, ONE, ZERO, merkle::EmptySubtreeRoots};
408
409    /// Tests that a partial SMT constructed from a root is well behaved and returns expected
410    /// values.
411    #[test]
412    fn partial_smt_new_with_no_entries() {
413        let key0 = Word::from(rand_array::<Felt, 4>());
414        let value0 = Word::from(rand_array::<Felt, 4>());
415        let full = Smt::with_entries([(key0, value0)]).unwrap();
416
417        let partial_smt = PartialSmt::new(full.root());
418
419        assert!(!partial_smt.tracks_leaves());
420        assert_eq!(partial_smt.num_entries(), 0);
421        assert_eq!(partial_smt.num_leaves(), 0);
422        assert_eq!(partial_smt.entries().count(), 0);
423        assert_eq!(partial_smt.tracked_leaves().count(), 0);
424        assert_eq!(partial_smt.root(), full.root());
425    }
426
427    /// Tests that a basic PartialSmt can be built from a full one and that inserting or removing
428    /// values whose merkle path were added to the partial SMT results in the same root as the
429    /// equivalent update in the full tree.
430    #[test]
431    fn partial_smt_insert_and_remove() {
432        let key0 = Word::from(rand_array::<Felt, 4>());
433        let key1 = Word::from(rand_array::<Felt, 4>());
434        let key2 = Word::from(rand_array::<Felt, 4>());
435        // A key for which we won't add a value so it will be empty.
436        let key_empty = Word::from(rand_array::<Felt, 4>());
437
438        let value0 = Word::from(rand_array::<Felt, 4>());
439        let value1 = Word::from(rand_array::<Felt, 4>());
440        let value2 = Word::from(rand_array::<Felt, 4>());
441
442        let mut kv_pairs = vec![(key0, value0), (key1, value1), (key2, value2)];
443
444        // Add more random leaves.
445        kv_pairs.reserve(1000);
446        for _ in 0..1000 {
447            let key = Word::from(rand_array::<Felt, 4>());
448            let value = Word::from(rand_array::<Felt, 4>());
449            kv_pairs.push((key, value));
450        }
451
452        let mut full = Smt::with_entries(kv_pairs).unwrap();
453
454        // Constructing a partial SMT from proofs succeeds.
455        // ----------------------------------------------------------------------------------------
456
457        let proof0 = full.open(&key0);
458        let proof2 = full.open(&key2);
459        let proof_empty = full.open(&key_empty);
460
461        assert!(proof_empty.leaf().is_empty());
462
463        let mut partial = PartialSmt::from_proofs([proof0, proof2, proof_empty]).unwrap();
464
465        assert_eq!(full.root(), partial.root());
466        assert_eq!(partial.get_value(&key0).unwrap(), value0);
467        let error = partial.get_value(&key1).unwrap_err();
468        assert_matches!(error, MerkleError::UntrackedKey(_));
469        assert_eq!(partial.get_value(&key2).unwrap(), value2);
470
471        // Insert new values for added keys with empty and non-empty values.
472        // ----------------------------------------------------------------------------------------
473
474        let new_value0 = Word::from(rand_array::<Felt, 4>());
475        let new_value2 = Word::from(rand_array::<Felt, 4>());
476        // A non-empty value for the key that was previously empty.
477        let new_value_empty_key = Word::from(rand_array::<Felt, 4>());
478
479        full.insert(key0, new_value0).unwrap();
480        full.insert(key2, new_value2).unwrap();
481        full.insert(key_empty, new_value_empty_key).unwrap();
482
483        partial.insert(key0, new_value0).unwrap();
484        partial.insert(key2, new_value2).unwrap();
485        // This updates a key whose value was previously empty.
486        partial.insert(key_empty, new_value_empty_key).unwrap();
487
488        assert_eq!(full.root(), partial.root());
489        assert_eq!(partial.get_value(&key0).unwrap(), new_value0);
490        assert_eq!(partial.get_value(&key2).unwrap(), new_value2);
491        assert_eq!(partial.get_value(&key_empty).unwrap(), new_value_empty_key);
492
493        // Remove an added key.
494        // ----------------------------------------------------------------------------------------
495
496        full.insert(key0, EMPTY_WORD).unwrap();
497        partial.insert(key0, EMPTY_WORD).unwrap();
498
499        assert_eq!(full.root(), partial.root());
500        assert_eq!(partial.get_value(&key0).unwrap(), EMPTY_WORD);
501
502        // Check if returned openings are the same in partial and full SMT.
503        // ----------------------------------------------------------------------------------------
504
505        // This is a key whose value is empty since it was removed.
506        assert_eq!(full.open(&key0), partial.open(&key0).unwrap());
507        // This is a key whose value is non-empty.
508        assert_eq!(full.open(&key2), partial.open(&key2).unwrap());
509
510        // Attempting to update a key whose merkle path was not added is an error.
511        // ----------------------------------------------------------------------------------------
512
513        let error = partial.clone().insert(key1, Word::from(rand_array::<Felt, 4>())).unwrap_err();
514        assert_matches!(error, MerkleError::UntrackedKey(_));
515
516        let error = partial.insert(key1, EMPTY_WORD).unwrap_err();
517        assert_matches!(error, MerkleError::UntrackedKey(_));
518    }
519
520    /// Test that we can add an SmtLeaf::Multiple variant to a partial SMT.
521    #[test]
522    fn partial_smt_multiple_leaf_success() {
523        // key0 and key1 have the same felt at index 3 so they will be placed in the same leaf.
524        let key0 = Word::from([ZERO, ZERO, ZERO, ONE]);
525        let key1 = Word::from([ONE, ONE, ONE, ONE]);
526        let key2 = Word::from(rand_array::<Felt, 4>());
527
528        let value0 = Word::from(rand_array::<Felt, 4>());
529        let value1 = Word::from(rand_array::<Felt, 4>());
530        let value2 = Word::from(rand_array::<Felt, 4>());
531
532        let full = Smt::with_entries([(key0, value0), (key1, value1), (key2, value2)]).unwrap();
533
534        // Make sure our assumption about the leaf being a multiple is correct.
535        let SmtLeaf::Multiple(_) = full.get_leaf(&key0) else {
536            panic!("expected full tree to produce multiple leaf")
537        };
538
539        let proof0 = full.open(&key0);
540        let proof2 = full.open(&key2);
541
542        let partial = PartialSmt::from_proofs([proof0, proof2]).unwrap();
543
544        assert_eq!(partial.root(), full.root());
545
546        assert_eq!(partial.get_leaf(&key0).unwrap(), full.get_leaf(&key0));
547        // key1 is present in the partial tree because it is part of the proof of key0.
548        assert_eq!(partial.get_leaf(&key1).unwrap(), full.get_leaf(&key1));
549        assert_eq!(partial.get_leaf(&key2).unwrap(), full.get_leaf(&key2));
550    }
551
552    /// Tests that adding proofs to a partial SMT whose roots are not the same will result in an
553    /// error.
554    ///
555    /// This test uses only empty values in the partial SMT.
556    #[test]
557    fn partial_smt_root_mismatch_on_empty_values() {
558        let key0 = Word::from(rand_array::<Felt, 4>());
559        let key1 = Word::from(rand_array::<Felt, 4>());
560        let key2 = Word::from(rand_array::<Felt, 4>());
561
562        let value0 = EMPTY_WORD;
563        let value1 = Word::from(rand_array::<Felt, 4>());
564        let value2 = EMPTY_WORD;
565
566        let kv_pairs = vec![(key0, value0)];
567
568        let mut full = Smt::with_entries(kv_pairs).unwrap();
569
570        // This proof will become stale after the tree is modified.
571        let stale_proof = full.open(&key2);
572
573        // Insert a non-empty value so the root actually changes.
574        full.insert(key1, value1).unwrap();
575        full.insert(key2, value2).unwrap();
576
577        // Construct a partial SMT against the latest root.
578        let mut partial = PartialSmt::new(full.root());
579
580        // Adding the stale proof should fail as its root is different.
581        let err = partial.add_proof(stale_proof).unwrap_err();
582        assert_matches!(err, MerkleError::ConflictingRoots { .. });
583    }
584
585    /// Tests that adding proofs to a partial SMT whose roots are not the same will result in an
586    /// error.
587    ///
588    /// This test uses only non-empty values in the partial SMT.
589    #[test]
590    fn partial_smt_root_mismatch_on_non_empty_values() {
591        let key0 = Word::new(rand_array());
592        let key1 = Word::new(rand_array());
593        let key2 = Word::new(rand_array());
594
595        let value0 = Word::new(rand_array());
596        let value1 = Word::new(rand_array());
597        let value2 = Word::new(rand_array());
598
599        let kv_pairs = vec![(key0, value0), (key1, value1)];
600
601        let mut full = Smt::with_entries(kv_pairs).unwrap();
602
603        // This proof will become stale after the tree is modified.
604        let stale_proof = full.open(&key0);
605
606        // Insert a value so the root changes.
607        full.insert(key2, value2).unwrap();
608
609        // Construct a partial SMT against the latest root.
610        let mut partial = PartialSmt::new(full.root());
611
612        // Adding the stale proof should fail as its root is different.
613        let err = partial.add_proof(stale_proof).unwrap_err();
614        assert_matches!(err, MerkleError::ConflictingRoots { .. });
615    }
616
617    /// Tests that from_proofs fails when the proofs roots do not match.
618    #[test]
619    fn partial_smt_from_proofs_fails_on_root_mismatch() {
620        let key0 = Word::new(rand_array());
621        let key1 = Word::new(rand_array());
622
623        let value0 = Word::new(rand_array());
624        let value1 = Word::new(rand_array());
625
626        let mut full = Smt::with_entries([(key0, value0)]).unwrap();
627
628        // This proof will become stale after the tree is modified.
629        let stale_proof = full.open(&key0);
630
631        // Insert a value so the root changes.
632        full.insert(key1, value1).unwrap();
633
634        // Construct a partial SMT against the latest root.
635        let err = PartialSmt::from_proofs([full.open(&key1), stale_proof]).unwrap_err();
636        assert_matches!(err, MerkleError::ConflictingRoots { .. });
637    }
638
639    /// Tests that a basic PartialSmt's iterator APIs return the expected values.
640    #[test]
641    fn partial_smt_iterator_apis() {
642        let key0 = Word::new(rand_array());
643        let key1 = Word::new(rand_array());
644        let key2 = Word::new(rand_array());
645        // A key for which we won't add a value so it will be empty.
646        let key_empty = Word::new(rand_array());
647
648        let value0 = Word::new(rand_array());
649        let value1 = Word::new(rand_array());
650        let value2 = Word::new(rand_array());
651
652        let mut kv_pairs = vec![(key0, value0), (key1, value1), (key2, value2)];
653
654        // Add more random leaves.
655        kv_pairs.reserve(1000);
656        for _ in 0..1000 {
657            let key = Word::new(rand_array());
658            let value = Word::new(rand_array());
659            kv_pairs.push((key, value));
660        }
661
662        let full = Smt::with_entries(kv_pairs).unwrap();
663
664        // Construct a partial SMT from proofs.
665        // ----------------------------------------------------------------------------------------
666
667        let proof0 = full.open(&key0);
668        let proof2 = full.open(&key2);
669        let proof_empty = full.open(&key_empty);
670
671        assert!(proof_empty.leaf().is_empty());
672
673        let proofs = [proof0, proof2, proof_empty];
674        let partial = PartialSmt::from_proofs(proofs.clone()).unwrap();
675
676        assert!(partial.tracks_leaves());
677        assert_eq!(full.root(), partial.root());
678        // There should be 2 non-empty entries.
679        assert_eq!(partial.num_entries(), 2);
680        // There should be 3 leaves, including the empty one.
681        assert_eq!(partial.num_leaves(), 3);
682
683        // The leaves API should only return tracked but non-empty leaves.
684        // ----------------------------------------------------------------------------------------
685
686        // Construct the sorted vector of leaves that should be yielded by the partial SMT.
687        let expected_leaves: BTreeMap<_, _> =
688            [SmtLeaf::new_single(key0, value0), SmtLeaf::new_single(key2, value2)]
689                .into_iter()
690                .map(|leaf| (leaf.index(), leaf))
691                .collect();
692
693        let actual_leaves = partial
694            .leaves()
695            .map(|(idx, leaf)| (idx, leaf.clone()))
696            .collect::<BTreeMap<_, _>>();
697
698        assert_eq!(actual_leaves.len(), expected_leaves.len());
699        assert_eq!(actual_leaves, expected_leaves);
700
701        // The tracked_leaves API should return all tracked leaves, including empty ones.
702        // ----------------------------------------------------------------------------------------
703
704        let mut expected_tracked_leaves = expected_leaves;
705        let empty_leaf = SmtLeaf::new_empty(LeafIndex::from(key_empty));
706        expected_tracked_leaves.insert(empty_leaf.index(), empty_leaf);
707
708        let actual_tracked_leaves = partial
709            .tracked_leaves()
710            .map(|(idx, leaf)| (idx, leaf.clone()))
711            .collect::<BTreeMap<_, _>>();
712
713        assert_eq!(actual_tracked_leaves.len(), expected_tracked_leaves.len());
714        assert_eq!(actual_tracked_leaves, expected_tracked_leaves);
715
716        // The entries of the merkle paths from the proofs should exist as children of inner nodes
717        // in the partial SMT.
718        // ----------------------------------------------------------------------------------------
719
720        let partial_inner_nodes: BTreeSet<_> =
721            partial.inner_nodes().flat_map(|node| [node.left, node.right]).collect();
722        let empty_subtree_roots: BTreeSet<_> = (0..SMT_DEPTH)
723            .map(|depth| *EmptySubtreeRoots::entry(SMT_DEPTH, depth))
724            .collect();
725
726        for merkle_path in proofs.into_iter().map(|proof| proof.into_parts().0) {
727            for (idx, digest) in merkle_path.into_iter().enumerate() {
728                assert!(
729                    partial_inner_nodes.contains(&digest) || empty_subtree_roots.contains(&digest),
730                    "failed at idx {idx}"
731                );
732            }
733        }
734    }
735
736    /// Test that the default partial SMT's tracks_leaves method returns `false`.
737    #[test]
738    fn partial_smt_tracks_leaves() {
739        assert!(!PartialSmt::default().tracks_leaves());
740    }
741
742    /// `PartialSmt` serde round-trip. Also tests conversion from SMT.
743    #[test]
744    fn partial_smt_serialization_roundtrip() {
745        let key = rand_value();
746        let val = rand_value();
747
748        let key_1 = rand_value();
749        let val_1 = rand_value();
750
751        let key_2 = rand_value();
752        let val_2 = rand_value();
753
754        let smt: Smt = Smt::with_entries([(key, val), (key_1, val_1), (key_2, val_2)]).unwrap();
755
756        let partial_smt = PartialSmt::from_proofs([smt.open(&key)]).unwrap();
757
758        assert_eq!(partial_smt.root(), smt.root());
759        assert_matches!(partial_smt.open(&key_1), Err(MerkleError::UntrackedKey(_)));
760        assert_matches!(partial_smt.open(&key), Ok(_));
761
762        let bytes = partial_smt.to_bytes();
763        let decoded = PartialSmt::read_from_bytes(&bytes).unwrap();
764
765        assert_eq!(partial_smt, decoded);
766    }
767
768    /// Tests that add_path correctly updates num_entries for increasing entry counts.
769    ///
770    /// Note that decreasing counts are not possible with the current API.
771    #[test]
772    fn partial_smt_add_proof_num_entries() {
773        // key0 and key1 have the same felt at index 3 so they will be placed in the same leaf.
774        let key0 = Word::from([ZERO, ZERO, ZERO, ONE]);
775        let key1 = Word::from([ONE, ONE, ONE, ONE]);
776        let key2 = Word::from([ONE, ONE, ONE, Felt::new(5)]);
777        let value0 = Word::from(rand_array::<Felt, 4>());
778        let value1 = Word::from(rand_array::<Felt, 4>());
779        let value2 = Word::from(rand_array::<Felt, 4>());
780
781        let full = Smt::with_entries([(key0, value0), (key1, value1), (key2, value2)]).unwrap();
782        let mut partial = PartialSmt::new(full.root());
783
784        // Add the multi-entry leaf
785        partial.add_proof(full.open(&key0)).unwrap();
786        assert_eq!(partial.num_entries(), 2);
787
788        // Add the single-entry leaf
789        partial.add_proof(full.open(&key2)).unwrap();
790        assert_eq!(partial.num_entries(), 3);
791
792        // Setting a value to the empty word removes decreases the number of entries.
793        partial.insert(key0, Word::empty()).unwrap();
794        assert_eq!(partial.num_entries(), 2);
795    }
796}