diff --git a/miden-crypto/src/merkle/index.rs b/miden-crypto/src/merkle/index.rs index ae27fec..39554c7 100644 --- a/miden-crypto/src/merkle/index.rs +++ b/miden-crypto/src/merkle/index.rs @@ -164,6 +164,18 @@ impl NodeIndex { self.depth = self.depth.saturating_sub(delta); self.value >>= delta as u32; } + + // ITERATORS + // -------------------------------------------------------------------------------------------- + + /// Return an iterator of the indices required for a Merkle proof of inclusion of a node at + /// `self`. + /// + /// This is *exclusive* on both ends: neither `self` nor the root index are included in the + /// returned iterator. + pub fn proof_indices(&self) -> impl ExactSizeIterator + use<> { + ProofIter { next_index: self.sibling() } + } } impl Display for NodeIndex { @@ -188,6 +200,39 @@ impl Deserializable for NodeIndex { } } +/// Implementation for [`NodeIndex::proof_indices()`]. +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] +struct ProofIter { + next_index: NodeIndex, +} + +impl Iterator for ProofIter { + type Item = NodeIndex; + + fn next(&mut self) -> Option { + if self.next_index.is_root() { + return None; + } + + let index = self.next_index; + self.next_index = index.parent().sibling(); + + Some(index) + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = ExactSizeIterator::len(self); + + (remaining, Some(remaining)) + } +} + +impl ExactSizeIterator for ProofIter { + fn len(&self) -> usize { + self.next_index.depth() as usize + } +} + #[cfg(test)] mod tests { use assert_matches::assert_matches; diff --git a/miden-crypto/src/merkle/smt/mod.rs b/miden-crypto/src/merkle/smt/mod.rs index 03f1bed..6369540 100644 --- a/miden-crypto/src/merkle/smt/mod.rs +++ b/miden-crypto/src/merkle/smt/mod.rs @@ -79,28 +79,34 @@ pub(crate) trait SparseMerkleTree { // PROVIDED METHODS // --------------------------------------------------------------------------------------------- + /// Returns a [MerklePath] to the specified key. + /// + /// Mostly this is an implementation detail of [`Self::open()`]. + fn get_path(&self, key: &Self::Key) -> MerklePath { + let index = NodeIndex::from(Self::key_to_leaf_index(key)); + index.proof_indices().map(|index| self.get_hash(index)).collect() + } + + /// Get the hash of a node at an arbitrary index, including the root or leaf hashes. + /// + /// The root index simply returns [`Self::root()`]. Other hashes are retrieved by calling + /// [`Self::get_inner_node()`] on the parent, and returning the respective child hash. + fn get_hash(&self, index: NodeIndex) -> RpoDigest { + if index.is_root() { + return self.root(); + } + + let InnerNode { left, right } = self.get_inner_node(index.parent()); + + let index_is_right = index.is_value_odd(); + if index_is_right { right } else { left } + } + /// Returns an opening of the leaf associated with `key`. Conceptually, an opening is a Merkle /// path to the leaf, as well as the leaf itself. fn open(&self, key: &Self::Key) -> Self::Opening { let leaf = self.get_leaf(key); - - let mut index: NodeIndex = { - let leaf_index: LeafIndex = Self::key_to_leaf_index(key); - leaf_index.into() - }; - - let merkle_path = { - let mut path = Vec::with_capacity(index.depth() as usize); - for _ in 0..index.depth() { - let is_right = index.is_value_odd(); - index.move_up(); - let InnerNode { left, right } = self.get_inner_node(index); - let value = if is_right { left } else { right }; - path.push(value); - } - - MerklePath::new(path) - }; + let merkle_path = self.get_path(key); Self::path_and_leaf_to_opening(merkle_path, leaf) }