arnaucube
arnaucube/miden-cryptoPublic Self-hosted repository
miden-crypto/src/merkle/tiered_smt/proof.rs
src/merkle/tiered_smt/proof.rsRaw
  1use super::{
  2    get_common_prefix_tier_depth, get_key_prefix, hash_bottom_leaf, hash_upper_leaf,
  3    EmptySubtreeRoots, LeafNodeIndex, MerklePath, RpoDigest, TieredSmtProofError, Vec, Word,
  4};
  5
  6// CONSTANTS
  7// ================================================================================================
  8
  9/// Maximum node depth. This is also the bottom tier of the tree.
 10const MAX_DEPTH: u8 = super::TieredSmt::MAX_DEPTH;
 11
 12/// Value of an empty leaf.
 13pub const EMPTY_VALUE: Word = super::TieredSmt::EMPTY_VALUE;
 14
 15/// Depths at which leaves can exist in a tiered SMT.
 16pub const TIER_DEPTHS: [u8; 4] = super::TieredSmt::TIER_DEPTHS;
 17
 18// TIERED SPARSE MERKLE TREE PROOF
 19// ================================================================================================
 20
 21/// A proof which can be used to assert membership (or non-membership) of key-value pairs in a
 22/// Tiered Sparse Merkle tree.
 23///
 24/// The proof consists of a Merkle path and one or more key-value entries which describe the node
 25/// located at the base of the path. If the node at the base of the path resolves to [ZERO; 4],
 26/// the entries will contain a single item with value set to [ZERO; 4].
 27#[derive(PartialEq, Eq, Debug)]
 28pub struct TieredSmtProof {
 29    path: MerklePath,
 30    entries: Vec<(RpoDigest, Word)>,
 31}
 32
 33impl TieredSmtProof {
 34    // CONSTRUCTOR
 35    // --------------------------------------------------------------------------------------------
 36    /// Returns a new instance of [TieredSmtProof] instantiated from the specified path and entries.
 37    ///
 38    /// # Panics
 39    /// Panics if:
 40    /// - The length of the path is greater than 64.
 41    /// - Entries is an empty vector.
 42    /// - Entries contains more than 1 item, but the length of the path is not 64.
 43    /// - Entries contains more than 1 item, and one of the items has value set to [ZERO; 4].
 44    /// - Entries contains multiple items with keys which don't share the same 64-bit prefix.
 45    pub fn new<I>(path: MerklePath, entries: I) -> Result<Self, TieredSmtProofError>
 46    where
 47        I: IntoIterator<Item = (RpoDigest, Word)>,
 48    {
 49        let entries: Vec<(RpoDigest, Word)> = entries.into_iter().collect();
 50
 51        if !TIER_DEPTHS.into_iter().any(|e| e == path.depth()) {
 52            return Err(TieredSmtProofError::NotATierPath(path.depth()));
 53        }
 54
 55        if entries.is_empty() {
 56            return Err(TieredSmtProofError::EntriesEmpty);
 57        }
 58
 59        if entries.len() > 1 {
 60            if path.depth() != MAX_DEPTH {
 61                return Err(TieredSmtProofError::MultipleEntriesOutsideLastTier);
 62            }
 63
 64            let prefix = get_key_prefix(&entries[0].0);
 65            for entry in entries.iter().skip(1) {
 66                if entry.1 == EMPTY_VALUE {
 67                    return Err(TieredSmtProofError::EmptyValueNotAllowed);
 68                }
 69                let current = get_key_prefix(&entry.0);
 70                if prefix != current {
 71                    return Err(TieredSmtProofError::MismatchedPrefixes(prefix, current));
 72                }
 73            }
 74        }
 75
 76        Ok(Self { path, entries })
 77    }
 78
 79    // PROOF VERIFIER
 80    // --------------------------------------------------------------------------------------------
 81
 82    /// Returns true if a Tiered Sparse Merkle tree with the specified root contains the provided
 83    /// key-value pair.
 84    ///
 85    /// Note: this method cannot be used to assert non-membership. That is, if false is returned,
 86    /// it does not mean that the provided key-value pair is not in the tree.
 87    pub fn verify_membership(&self, key: &RpoDigest, value: &Word, root: &RpoDigest) -> bool {
 88        // Handles the following scenarios:
 89        // - the value is set
 90        // - empty leaf, there is an explicit entry for the key with the empty value
 91        // - shared 64-bit prefix, the target key is not included in the entries list, the value is implicitly the empty word
 92        let v = match self.entries.iter().find(|(k, _)| k == key) {
 93            Some((_, v)) => v,
 94            None => &EMPTY_VALUE,
 95        };
 96
 97        // The value must match for the proof to be valid
 98        if v != value {
 99            return false;
100        }
101
102        // If the proof is for an empty value, we can verify it against any key which has a common
103        // prefix with the key storied in entries, but the prefix must be greater than the path
104        // length
105        if self.is_value_empty()
106            && get_common_prefix_tier_depth(key, &self.entries[0].0) < self.path.depth()
107        {
108            return false;
109        }
110
111        // make sure the Merkle path resolves to the correct root
112        root == &self.compute_root()
113    }
114
115    // PUBLIC ACCESSORS
116    // --------------------------------------------------------------------------------------------
117
118    /// Returns the value associated with the specific key according to this proof, or None if
119    /// this proof does not contain a value for the specified key.
120    ///
121    /// A key-value pair generated by using this method should pass the `verify_membership()` check.
122    pub fn get(&self, key: &RpoDigest) -> Option<Word> {
123        if self.is_value_empty() {
124            let common_prefix_tier = get_common_prefix_tier_depth(key, &self.entries[0].0);
125            if common_prefix_tier < self.path.depth() {
126                None
127            } else {
128                Some(EMPTY_VALUE)
129            }
130        } else {
131            self.entries.iter().find(|(k, _)| k == key).map(|(_, value)| *value)
132        }
133    }
134
135    /// Computes the root of a Tiered Sparse Merkle tree to which this proof resolve.
136    pub fn compute_root(&self) -> RpoDigest {
137        let node = self.build_node();
138        let index = LeafNodeIndex::from_key(&self.entries[0].0, self.path.depth());
139        self.path
140            .compute_root(index.value(), node)
141            .expect("failed to compute Merkle path root")
142    }
143
144    /// Consume the proof and returns its parts.
145    pub fn into_parts(self) -> (MerklePath, Vec<(RpoDigest, Word)>) {
146        (self.path, self.entries)
147    }
148
149    // HELPER METHODS
150    // --------------------------------------------------------------------------------------------
151
152    /// Returns true if the proof is for an empty value.
153    fn is_value_empty(&self) -> bool {
154        self.entries[0].1 == EMPTY_VALUE
155    }
156
157    /// Converts the entries contained in this proof into a node value for node at the base of the
158    /// path contained in this proof.
159    fn build_node(&self) -> RpoDigest {
160        let depth = self.path.depth();
161        if self.is_value_empty() {
162            EmptySubtreeRoots::empty_hashes(MAX_DEPTH)[depth as usize]
163        } else if depth == MAX_DEPTH {
164            hash_bottom_leaf(&self.entries)
165        } else {
166            let (key, value) = self.entries[0];
167            hash_upper_leaf(key, value, depth)
168        }
169    }
170}