miden_crypto/merkle/smt/partial/mod.rs
1use alloc::{
2 collections::{BTreeMap, BTreeSet, VecDeque},
3 string::ToString,
4 vec::Vec,
5};
6
7use super::{EmptySubtreeRoots, LeafIndex, SMT_DEPTH};
8use crate::{
9 EMPTY_WORD, Map, Word,
10 merkle::{
11 InnerNodeInfo, MerkleError, NodeIndex, SparseMerklePath,
12 smt::{InnerNode, InnerNodes, Leaves, SmtLeaf, SmtLeafError, SmtProof},
13 },
14 utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
15};
16
17mod serialization;
18#[cfg(test)]
19mod tests;
20
21pub use serialization::UniqueNodes;
22
23/// A partial version of an [`super::Smt`].
24///
25/// This type can track a subset of the key-value pairs of a full [`super::Smt`] and allows for
26/// updating those pairs to compute the new root of the tree, as if the updates had been done on the
27/// full tree. This is useful so that not all leaves have to be present and loaded into memory to
28/// compute an update.
29///
30/// A key is considered "tracked" if either:
31/// 1. Its merkle path was explicitly added to the tree (via [`PartialSmt::add_path`] or
32/// [`PartialSmt::add_proof`]), or
33/// 2. The path from the leaf to the root goes through empty subtrees that are consistent with the
34/// stored inner nodes (provably empty with zero hash computations).
35///
36/// The second condition allows updating keys in empty subtrees without explicitly adding their
37/// merkle paths. This is verified by walking up from the leaf and checking that any stored
38/// inner node has an empty subtree root as the child on our path.
39///
40/// An important caveat is that only tracked keys can be updated. Attempting to update an
41/// untracked key will result in an error. See [`PartialSmt::insert`] for more details.
42///
43/// Once a partial SMT has been constructed, its root is set in stone. All subsequently added proofs
44/// or merkle paths must match that root, otherwise an error is returned.
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct PartialSmt {
47 root: Word,
48 num_entries: usize,
49 leaves: Leaves<SmtLeaf>,
50 inner_nodes: InnerNodes,
51}
52
53impl PartialSmt {
54 // CONSTANTS
55 // --------------------------------------------------------------------------------------------
56
57 /// The default value used to compute the hash of empty leaves.
58 pub const EMPTY_VALUE: Word = EMPTY_WORD;
59
60 /// The root of an empty tree.
61 pub const EMPTY_ROOT: Word = *EmptySubtreeRoots::entry(SMT_DEPTH, 0);
62
63 // CONSTRUCTORS
64 // --------------------------------------------------------------------------------------------
65
66 /// Constructs a [`PartialSmt`] from a root.
67 ///
68 /// All subsequently added proofs or paths must have the same root.
69 pub fn new(root: Word) -> Self {
70 Self {
71 root,
72 num_entries: 0,
73 leaves: Leaves::<SmtLeaf>::default(),
74 inner_nodes: InnerNodes::default(),
75 }
76 }
77
78 /// Instantiates a new [`PartialSmt`] by calling [`PartialSmt::add_proof`] for all [`SmtProof`]s
79 /// in the provided iterator.
80 ///
81 /// If the provided iterator is empty, an empty [`PartialSmt`] is returned.
82 ///
83 /// # Errors
84 ///
85 /// Returns an error if:
86 /// - the roots of the provided proofs are not the same.
87 pub fn from_proofs<I>(proofs: I) -> Result<Self, MerkleError>
88 where
89 I: IntoIterator<Item = SmtProof>,
90 {
91 let mut proofs = proofs.into_iter();
92
93 let Some(first_proof) = proofs.next() else {
94 return Ok(Self::default());
95 };
96
97 // Add the first path to an empty partial SMT without checking that the existing root
98 // matches the new one. This sets the expected root to the root of the first proof and all
99 // subsequently added proofs must match it.
100 let mut partial_smt = Self::default();
101 let (path, leaf) = first_proof.into_parts();
102 let path_root = partial_smt.add_path_unchecked(leaf, path);
103 partial_smt.root = path_root;
104
105 for proof in proofs {
106 partial_smt.add_proof(proof)?;
107 }
108
109 Ok(partial_smt)
110 }
111
112 // PUBLIC ACCESSORS
113 // --------------------------------------------------------------------------------------------
114
115 /// Returns the root of the tree.
116 pub fn root(&self) -> Word {
117 self.root
118 }
119
120 /// Returns an opening of the leaf associated with `key`. Conceptually, an opening is a Merkle
121 /// path to the leaf, as well as the leaf itself.
122 ///
123 /// # Errors
124 ///
125 /// Returns an error if:
126 /// - the key is not tracked by this partial SMT.
127 pub fn open(&self, key: &Word) -> Result<SmtProof, MerkleError> {
128 let leaf = self.get_leaf(key)?;
129 let merkle_path = self.get_path(key);
130 Ok(SmtProof::new_unchecked(merkle_path, leaf))
131 }
132
133 /// Returns the leaf to which `key` maps.
134 ///
135 /// # Errors
136 ///
137 /// Returns an error if:
138 /// - the key is not tracked by this partial SMT.
139 pub fn get_leaf(&self, key: &Word) -> Result<SmtLeaf, MerkleError> {
140 self.get_tracked_leaf(key).ok_or(MerkleError::UntrackedKey(*key))
141 }
142
143 /// Returns the value associated with `key`.
144 ///
145 /// # Errors
146 ///
147 /// Returns an error if:
148 /// - the key is not tracked by this partial SMT.
149 pub fn get_value(&self, key: &Word) -> Result<Word, MerkleError> {
150 self.get_tracked_leaf(key)
151 .map(|leaf| leaf.get_value(key).unwrap_or_default())
152 .ok_or(MerkleError::UntrackedKey(*key))
153 }
154
155 /// Returns an iterator over the inner nodes of the [`PartialSmt`].
156 pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
157 self.inner_nodes.values().map(|e| InnerNodeInfo {
158 value: e.hash(),
159 left: e.left,
160 right: e.right,
161 })
162 }
163
164 /// Returns an iterator over the [`InnerNode`] and the respective [`NodeIndex`] of the
165 /// [`PartialSmt`].
166 pub fn inner_node_indices(&self) -> impl Iterator<Item = (NodeIndex, InnerNode)> + '_ {
167 self.inner_nodes.iter().map(|(idx, inner)| (*idx, inner.clone()))
168 }
169
170 /// Returns an iterator over the explicitly stored leaves of the [`PartialSmt`] in arbitrary
171 /// order.
172 ///
173 /// Note: This only returns leaves that were explicitly added via [`Self::add_path`] or
174 /// [`Self::add_proof`], or created through [`Self::insert`]. It does not include implicitly
175 /// trackable leaves in empty subtrees.
176 pub fn leaves(&self) -> impl Iterator<Item = (LeafIndex<SMT_DEPTH>, &SmtLeaf)> {
177 self.leaves
178 .iter()
179 .map(|(leaf_index, leaf)| (LeafIndex::new_max_depth(*leaf_index), leaf))
180 }
181
182 /// Returns an iterator over the tracked, non-empty key-value pairs of the [`PartialSmt`] in
183 /// arbitrary order.
184 pub fn entries(&self) -> impl Iterator<Item = &(Word, Word)> {
185 self.leaves().flat_map(|(_, leaf)| leaf.entries())
186 }
187
188 /// Returns the number of non-empty leaves in this tree.
189 ///
190 /// Note that this may return a different value from [Self::num_entries()] as a single leaf may
191 /// contain more than one key-value pair.
192 pub fn num_leaves(&self) -> usize {
193 self.leaves.len()
194 }
195
196 /// Returns the number of tracked, non-empty key-value pairs in this tree.
197 ///
198 /// Note that this may return a different value from [Self::num_leaves()] as a single leaf may
199 /// contain more than one key-value pair.
200 pub fn num_entries(&self) -> usize {
201 self.num_entries
202 }
203
204 /// Returns a boolean value indicating whether the [`PartialSmt`] tracks any leaves.
205 ///
206 /// Note that if a partial SMT does not track leaves, its root is not necessarily the empty SMT
207 /// root, since it could have been constructed from a different root but without tracking any
208 /// leaves.
209 pub fn tracks_leaves(&self) -> bool {
210 !self.leaves.is_empty()
211 }
212
213 // STATE MUTATORS
214 // --------------------------------------------------------------------------------------------
215
216 /// Inserts a value at the specified key, returning the previous value associated with that key.
217 /// Recall that by definition, any key that hasn't been updated is associated with
218 /// [`Self::EMPTY_VALUE`].
219 ///
220 /// This also recomputes all hashes between the leaf (associated with the key) and the root,
221 /// updating the root itself.
222 ///
223 /// # Errors
224 ///
225 /// Returns an error if:
226 /// - the key is not tracked (see the type documentation for the definition of "tracked"). If an
227 /// error is returned the tree is in the same state as before.
228 /// - inserting the key-value pair would exceed [`super::MAX_LEAF_ENTRIES`] (1024 entries) in
229 /// the leaf.
230 pub fn insert(&mut self, key: Word, value: Word) -> Result<Word, MerkleError> {
231 let current_leaf = self.get_tracked_leaf(&key).ok_or(MerkleError::UntrackedKey(key))?;
232 let leaf_index = current_leaf.index();
233 let previous_value = current_leaf.get_value(&key).unwrap_or(EMPTY_WORD);
234 let prev_entries = current_leaf.num_entries();
235
236 let leaf = self
237 .leaves
238 .entry(leaf_index.position())
239 .or_insert_with(|| SmtLeaf::new_empty(leaf_index));
240
241 if value != EMPTY_WORD {
242 leaf.insert(key, value).map_err(|e| match e {
243 SmtLeafError::TooManyLeafEntries { actual } => {
244 MerkleError::TooManyLeafEntries { actual }
245 },
246 other => panic!("unexpected SmtLeaf::insert error: {other:?}"),
247 })?;
248 } else {
249 leaf.remove(key);
250 }
251 let current_entries = leaf.num_entries();
252 let new_leaf_hash = leaf.hash();
253 self.num_entries = self.num_entries + current_entries - prev_entries;
254
255 // Remove empty leaf
256 if current_entries == 0 {
257 self.leaves.remove(&leaf_index.position());
258 }
259
260 // Recompute the path from leaf to root
261 self.recompute_nodes_from_leaf_to_root(leaf_index, new_leaf_hash);
262
263 Ok(previous_value)
264 }
265
266 /// Adds an [`SmtProof`] to this [`PartialSmt`].
267 ///
268 /// This is a convenience method which calls [`Self::add_path`] on the proof. See its
269 /// documentation for details on errors.
270 pub fn add_proof(&mut self, proof: SmtProof) -> Result<(), MerkleError> {
271 let (path, leaf) = proof.into_parts();
272 self.add_path(leaf, path)
273 }
274
275 /// Adds a leaf and its sparse merkle path to this [`PartialSmt`].
276 ///
277 /// If this function was called, any key that is part of the `leaf` can subsequently be updated
278 /// to a new value and produce a correct new tree root.
279 ///
280 /// # Errors
281 ///
282 /// Returns an error if:
283 /// - the new root after the insertion of the leaf and the path does not match the existing
284 /// root. If an error is returned, the tree is left in an inconsistent state.
285 pub fn add_path(&mut self, leaf: SmtLeaf, path: SparseMerklePath) -> Result<(), MerkleError> {
286 let path_root = self.add_path_unchecked(leaf, path);
287
288 // Check if the newly added merkle path is consistent with the existing tree. If not, the
289 // merkle path was invalid or computed against another tree.
290 if self.root() != path_root {
291 return Err(MerkleError::ConflictingRoots {
292 expected_root: self.root(),
293 actual_root: path_root,
294 });
295 }
296
297 Ok(())
298 }
299
300 // UNIQUE NODES
301 // --------------------------------------------------------------------------------------------
302
303 /// Converts `self` into the [`UniqueNodes`] serialization representation for compact
304 /// serialization.
305 ///
306 /// This method assumes that the `PartialSmt` is in a valid state.
307 ///
308 /// # Reconstructable Sets
309 ///
310 /// We define the notion of a reconstructable set as one which stores the minimum amount of
311 /// information necessary in order to reconstruct the full state of the tree. We build this set
312 /// as follows:
313 ///
314 /// 1. Start at the leaves and traverse toward the root.
315 /// 2. Wherever a node's value is determined solely by children already implicitly contained
316 /// within the set, store no new information. If additional information is required (e.g. a
317 /// sibling node) store that.
318 /// 3. Repeat until the root is reached.
319 ///
320 /// To reconstruct the tree, we just start at the leaves and compute all intermediary nodes from
321 /// the data stored in the reconstructible set.
322 pub fn to_unique_nodes(&self) -> UniqueNodes {
323 // We start by getting all the known leaves, as these give us the starting point for the
324 // reconstruction.
325 let leaf_nodes = self
326 .leaves()
327 .map(|(k, v)| (k, v.clone()))
328 .collect::<Map<LeafIndex<SMT_DEPTH>, SmtLeaf>>();
329
330 // We also create storage for the nodes necessary for reconstruction of the tree...
331 let mut needed_nodes = BTreeMap::new();
332
333 // ... and grab the full set of inner nodes to work from as a queue for easy use. We sort
334 // them from the bottom of the tree to the top, but retain the standard left-to-right
335 // ordering.
336 let mut inner_nodes = self.inner_node_indices().collect::<Vec<(NodeIndex, InnerNode)>>();
337 inner_nodes.sort_by(|(il, _), (ir, _)| {
338 ir.depth().cmp(&il.depth()).then(il.position().cmp(&ir.position()))
339 });
340 let mut inner_nodes = inner_nodes.into_iter().collect::<VecDeque<(NodeIndex, InnerNode)>>();
341
342 // We also need to store the values for leaves where we ONLY have the hash value, rather
343 // than the proper leaf value.
344 let mut value_only_leaves = BTreeMap::new();
345
346 // We then need to iterate over all the nodes to work out which ones are reconstructible,
347 // and which need us to store additional data to be reconstructible.
348 while let Some((ix, v)) = inner_nodes.pop_front() {
349 // There must be data available for both of the node's children for it to be
350 // reconstructible.
351 for (child, val) in [(ix.left_child(), v.left), (ix.right_child(), v.right)] {
352 if child.depth() != SMT_DEPTH {
353 // A child of the node `v` can be in one of three states:
354 //
355 // 1. The child does not exist as a physical node in `self`, but its value as
356 // stored in `v` is real.
357 // 2. The child does not exist as a physical node in `self`, but its value is
358 // the default empty subtree root.
359 // 3. The child does exist as a physical node in `self`. By induction, as this
360 // algorithm runs bottom-up, the data to reconstruct the node already exists.
361 if self.get_inner_node(child).is_none() {
362 // In this case, the node does not exist physically, so we have to work out
363 // which of the other cases it is.
364 if val != *EmptySubtreeRoots::entry(SMT_DEPTH, child.depth())
365 && let Some(previous) = needed_nodes.insert(child, val)
366 {
367 assert_eq!(
368 previous, val,
369 "node was overwritten with a different value"
370 );
371 }
372 } else {
373 // Here, the node exists physically, so by induction, it is reconstructible.
374 }
375 } else {
376 // Here the child is a leaf node. Leaf nodes can be in one of three states:
377 //
378 // 1. A node that has the default empty value, in which case we encode it using
379 // absence in the compact representation.
380 // 2. A node that has a hash value, but that does not exist in the physical
381 // leaves in the PartialSmt. These are encoded using an auxiliary buffer to
382 // aid in reconstruction.
383 // 3. A node that exists in fully-materialized form. These are encoded with
384 // their full content.
385 //
386 // Cases 1 and 3 require no special handling here, as they are encoded with the
387 // leaves below. Case 2 needs us to take action here.
388 let empty_leaf_hash =
389 SmtLeaf::new_empty(LeafIndex::new_max_depth(child.position())).hash();
390
391 if val != empty_leaf_hash && !self.leaves.contains_key(&child.position()) {
392 // We are in case 2 here, as the value is not that of the empty leaf, nor is
393 // there a physical leaf stored in the tree for this. We store this leaf
394 // value in the auxiliary buffer so we can reconstruct correctly in this
395 // scenario.
396 value_only_leaves.insert(child.position(), val);
397 }
398 }
399 }
400 }
401
402 // With all the data gathered, we can convert our types as necessary to create our output.
403 let leaves = leaf_nodes.into_iter().map(|(i, l)| (i.position(), l)).collect();
404
405 UniqueNodes {
406 root: self.root(),
407 leaves,
408 nodes: needed_nodes,
409 value_only_leaves,
410 }
411 }
412
413 /// Constructs a new `PartialSmt` from the provided `unique_nodes`, reconstituting the full data
414 /// from the compact representation.
415 ///
416 /// This method assumes that the `unique_nodes` represent a valid `PartialSmt` instance.
417 ///
418 /// See the documentation of [`Self::to_unique_nodes`] for the reconstruction algorithm.
419 ///
420 /// # Errors
421 ///
422 /// - [`DeserializationError::InvalidValue`] if a leaf's map key does not match its embedded
423 /// index, or the reconstructed tree fails validation.
424 pub fn from_unique_nodes(unique_nodes: UniqueNodes) -> Result<Self, DeserializationError> {
425 unique_nodes.validate()?;
426
427 let mut smt = Self::new(unique_nodes.root);
428
429 // Reconstruction starts from every known leaf position. Stored internal nodes are also
430 // starting points because an exclusion proof may contain no leaf below them. Group these
431 // positions by depth so each layer can be rebuilt before its parent layer.
432 let mut active_by_depth = BTreeMap::<u8, BTreeSet<NodeIndex>>::new();
433
434 for &position in unique_nodes.leaves.keys().chain(unique_nodes.value_only_leaves.keys()) {
435 let index = NodeIndex::new(SMT_DEPTH, position)
436 .map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
437 active_by_depth.entry(SMT_DEPTH).or_default().insert(index);
438 }
439 for &index in unique_nodes.nodes.keys() {
440 active_by_depth.entry(index.depth()).or_default().insert(index);
441 }
442
443 // Rebuild the tree one layer at a time, from the deepest starting positions to the root.
444 for child_depth in (1..=SMT_DEPTH).rev() {
445 let Some(active_nodes) = active_by_depth.remove(&child_depth) else {
446 continue;
447 };
448
449 // Every active node requires its parent. The set removes shared parents before the
450 // next layer is built.
451 let parents = active_nodes.into_iter().map(NodeIndex::parent).collect::<BTreeSet<_>>();
452
453 for parent in &parents {
454 // A child is either a leaf, a node rebuilt on the prior pass, a stored sparse node,
455 // or an omitted empty subtree root. The lookup helpers handle the last two cases.
456 let [left, right] = [parent.left_child(), parent.right_child()].map(|child| {
457 if child.depth() == SMT_DEPTH {
458 unique_nodes.get_leaf_hash(child.position())
459 } else {
460 smt.get_inner_node(child)
461 .map(|node| node.hash())
462 .unwrap_or_else(|| unique_nodes.get_node_hash(child))
463 }
464 });
465 smt.insert_inner_node(*parent, InnerNode { left, right });
466 }
467
468 if child_depth > 1 {
469 // The parents become the active nodes for the next layer toward the root.
470 active_by_depth.entry(child_depth - 1).or_default().extend(parents);
471 }
472 }
473
474 for (position, leaf) in unique_nodes.leaves {
475 smt.num_entries += leaf.num_entries();
476 smt.leaves.insert(position, leaf);
477 }
478
479 smt.validate()?;
480
481 Ok(smt)
482 }
483
484 // PRIVATE HELPERS
485 // --------------------------------------------------------------------------------------------
486
487 /// Adds a leaf and its sparse merkle path to this [`PartialSmt`] and returns the root of the
488 /// inserted path.
489 ///
490 /// This does not check that the path root matches the existing root of the tree and if so, the
491 /// tree is left in an inconsistent state. This state can be made consistent again by setting
492 /// the root of the SMT to the path root.
493 fn add_path_unchecked(&mut self, leaf: SmtLeaf, path: SparseMerklePath) -> Word {
494 let mut current_index = leaf.index().index;
495
496 let mut node_hash_at_current_index = leaf.hash();
497
498 let prev_entries = self
499 .leaves
500 .get(¤t_index.position())
501 .map(SmtLeaf::num_entries)
502 .unwrap_or(0);
503 let current_entries = leaf.num_entries();
504 // Only store non-empty leaves
505 if current_entries > 0 {
506 self.leaves.insert(current_index.position(), leaf);
507 } else {
508 self.leaves.remove(¤t_index.position());
509 }
510
511 // Guaranteed not to over/underflow. All variables are <= MAX_LEAF_ENTRIES and result > 0.
512 self.num_entries = self.num_entries + current_entries - prev_entries;
513
514 for sibling_hash in path {
515 // Find the index of the sibling node and compute whether it is a left or right child.
516 let is_sibling_right = current_index.sibling().is_position_odd();
517
518 // Move the index up so it points to the parent of the current index and the sibling.
519 current_index.move_up();
520
521 // Construct the new parent node from the child that was updated and the sibling from
522 // the merkle path.
523 let new_parent_node = if is_sibling_right {
524 InnerNode {
525 left: node_hash_at_current_index,
526 right: sibling_hash,
527 }
528 } else {
529 InnerNode {
530 left: sibling_hash,
531 right: node_hash_at_current_index,
532 }
533 };
534
535 node_hash_at_current_index = new_parent_node.hash();
536
537 self.insert_inner_node(current_index, new_parent_node);
538 }
539
540 node_hash_at_current_index
541 }
542
543 /// Returns the leaf for a key if it can be tracked.
544 ///
545 /// A key is trackable if:
546 /// 1. It was explicitly added via `add_path`/`add_proof`, OR
547 /// 2. The path to the leaf goes through empty subtrees (provably empty)
548 ///
549 /// Returns `None` if the key cannot be tracked (path goes through non-empty
550 /// subtrees we don't have data for).
551 fn get_tracked_leaf(&self, key: &Word) -> Option<SmtLeaf> {
552 let leaf_index = Self::key_to_leaf_index(key);
553
554 // Explicitly stored leaves are always trackable
555 if let Some(leaf) = self.leaves.get(&leaf_index.position()) {
556 return Some(leaf.clone());
557 }
558
559 // Empty tree - all leaves implicitly trackable
560 if self.root == Self::EMPTY_ROOT {
561 return Some(SmtLeaf::new_empty(leaf_index));
562 }
563
564 // Walk from root down towards the leaf
565 let target: NodeIndex = leaf_index.into();
566 let mut index = NodeIndex::root();
567
568 for i in (0..SMT_DEPTH).rev() {
569 let inner_node = self.get_inner_node(index)?;
570
571 let is_right = target.is_nth_bit_odd(i);
572 let child_hash = if is_right { inner_node.right } else { inner_node.left };
573
574 // If child is empty subtree root, leaf is implicitly trackable
575 if child_hash == *EmptySubtreeRoots::entry(SMT_DEPTH, SMT_DEPTH - i) {
576 return Some(SmtLeaf::new_empty(leaf_index));
577 }
578
579 index = if is_right {
580 index.right_child()
581 } else {
582 index.left_child()
583 };
584 }
585
586 // Reached leaf level without finding empty subtree - can't track
587 None
588 }
589
590 /// Converts a key to a leaf index.
591 fn key_to_leaf_index(key: &Word) -> LeafIndex<SMT_DEPTH> {
592 let most_significant_felt = key[3];
593 LeafIndex::new_max_depth(most_significant_felt.as_canonical_u64())
594 }
595
596 /// Returns the inner node at the specified index, or `None` if not stored.
597 fn get_inner_node(&self, index: NodeIndex) -> Option<InnerNode> {
598 self.inner_nodes.get(&index).cloned()
599 }
600
601 /// Returns the inner node at the specified index, falling back to the empty subtree root
602 /// if not stored.
603 fn get_inner_node_or_empty(&self, index: NodeIndex) -> InnerNode {
604 self.get_inner_node(index)
605 .unwrap_or_else(|| EmptySubtreeRoots::get_inner_node(SMT_DEPTH, index.depth()))
606 }
607
608 /// Inserts an inner node at the specified index, or removes it if it equals the empty
609 /// subtree root.
610 fn insert_inner_node(&mut self, index: NodeIndex, inner_node: InnerNode) {
611 if inner_node == EmptySubtreeRoots::get_inner_node(SMT_DEPTH, index.depth()) {
612 self.inner_nodes.remove(&index);
613 } else {
614 self.inner_nodes.insert(index, inner_node);
615 }
616 }
617
618 /// Returns the merkle path for a key by walking up the tree from the leaf.
619 fn get_path(&self, key: &Word) -> SparseMerklePath {
620 let index = NodeIndex::from(Self::key_to_leaf_index(key));
621
622 // Use proof_indices to get sibling indices from leaf to root,
623 // and get each sibling's hash
624 SparseMerklePath::from_sized_iter(index.proof_indices().map(|idx| self.get_node_hash(idx)))
625 .expect("path should be valid since it's from a valid SMT")
626 }
627
628 /// Get the hash of a node at an arbitrary index, including the root or leaf hashes.
629 ///
630 /// The root index simply returns the root. Other hashes are retrieved by looking at
631 /// the parent inner node and returning the respective child hash.
632 fn get_node_hash(&self, index: NodeIndex) -> Word {
633 if index.is_root() {
634 return self.root;
635 }
636
637 let InnerNode { left, right } = self.get_inner_node_or_empty(index.parent());
638
639 if index.is_position_odd() { right } else { left }
640 }
641
642 /// Recomputes all inner nodes from a leaf up to the root after a leaf value change.
643 fn recompute_nodes_from_leaf_to_root(
644 &mut self,
645 leaf_index: LeafIndex<SMT_DEPTH>,
646 leaf_hash: Word,
647 ) {
648 use crate::hash::poseidon2::Poseidon2;
649
650 let mut index: NodeIndex = leaf_index.into();
651 let mut node_hash = leaf_hash;
652
653 for _ in (0..index.depth()).rev() {
654 let is_right = index.is_position_odd();
655 index.move_up();
656 let InnerNode { left, right } = self.get_inner_node_or_empty(index);
657 let (left, right) = if is_right {
658 (left, node_hash)
659 } else {
660 (node_hash, right)
661 };
662 node_hash = Poseidon2::merge(&[left, right]);
663
664 // insert_inner_node handles removing empty subtree roots
665 self.insert_inner_node(index, InnerNode { left, right });
666 }
667 self.root = node_hash;
668 }
669
670 /// Validates the internal structure during deserialization.
671 ///
672 /// Checks that:
673 /// - Each inner node's hash is consistent with its parent.
674 /// - Each leaf's hash is consistent with its parent inner node's left/right child.
675 fn validate(&self) -> Result<(), DeserializationError> {
676 // Validate each inner node is consistent with its parent
677 for (&idx, node) in &self.inner_nodes {
678 let node_hash = node.hash();
679 let expected_hash = self.get_node_hash(idx);
680
681 if node_hash != expected_hash {
682 return Err(DeserializationError::InvalidValue(
683 "inner node hash is inconsistent with parent".into(),
684 ));
685 }
686 }
687
688 // Validate each leaf's hash is consistent with its parent inner node
689 for (&leaf_pos, leaf) in &self.leaves {
690 let leaf_index = LeafIndex::<SMT_DEPTH>::new_max_depth(leaf_pos);
691 let node_index: NodeIndex = leaf_index.into();
692 let leaf_hash = leaf.hash();
693 let expected_hash = self.get_node_hash(node_index);
694
695 if leaf_hash != expected_hash {
696 return Err(DeserializationError::InvalidValue(
697 "leaf hash is inconsistent with parent inner node".into(),
698 ));
699 }
700 }
701
702 Ok(())
703 }
704}
705
706impl Default for PartialSmt {
707 /// Returns a new, empty [`PartialSmt`].
708 ///
709 /// All leaves in the returned tree are set to [`Self::EMPTY_VALUE`].
710 fn default() -> Self {
711 Self::new(Self::EMPTY_ROOT)
712 }
713}
714
715// CONVERSIONS
716// ================================================================================================
717
718impl From<super::Smt> for PartialSmt {
719 fn from(smt: super::Smt) -> Self {
720 Self {
721 root: smt.root(),
722 num_entries: smt.num_entries(),
723 leaves: smt.leaves().map(|(idx, leaf)| (idx.position(), leaf.clone())).collect(),
724 inner_nodes: smt.inner_node_indices().collect(),
725 }
726 }
727}
728
729// SERIALIZATION
730// ================================================================================================
731
732impl Serializable for PartialSmt {
733 fn write_into<W: ByteWriter>(&self, target: &mut W) {
734 let unique_rep = self.to_unique_nodes();
735 unique_rep.write_into(target);
736 }
737}
738
739impl Deserializable for PartialSmt {
740 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
741 let unique_rep = UniqueNodes::read_from(source)?;
742 PartialSmt::from_unique_nodes(unique_rep)
743 .map_err(|e| DeserializationError::InvalidValue(format!("{e}")))
744 }
745}