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