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        if self.is_value_empty() {
 89            if value != &EMPTY_VALUE {
 90                return false;
 91            }
 92            // if the proof is for an empty value, we can verify it against any key which has a
 93            // common prefix with the key storied in entries, but the prefix must be greater than
 94            // the path length
 95            let common_prefix_tier = get_common_prefix_tier_depth(key, &self.entries[0].0);
 96            if common_prefix_tier < self.path.depth() {
 97                return false;
 98            }
 99        } else if !self.entries.contains(&(*key, *value)) {
100            return false;
101        }
102
103        // make sure the Merkle path resolves to the correct root
104        root == &self.compute_root()
105    }
106
107    // PUBLIC ACCESSORS
108    // --------------------------------------------------------------------------------------------
109
110    /// Returns the value associated with the specific key according to this proof, or None if
111    /// this proof does not contain a value for the specified key.
112    ///
113    /// A key-value pair generated by using this method should pass the `verify_membership()` check.
114    pub fn get(&self, key: &RpoDigest) -> Option<Word> {
115        if self.is_value_empty() {
116            let common_prefix_tier = get_common_prefix_tier_depth(key, &self.entries[0].0);
117            if common_prefix_tier < self.path.depth() {
118                None
119            } else {
120                Some(EMPTY_VALUE)
121            }
122        } else {
123            self.entries.iter().find(|(k, _)| k == key).map(|(_, value)| *value)
124        }
125    }
126
127    /// Computes the root of a Tiered Sparse Merkle tree to which this proof resolve.
128    pub fn compute_root(&self) -> RpoDigest {
129        let node = self.build_node();
130        let index = LeafNodeIndex::from_key(&self.entries[0].0, self.path.depth());
131        self.path
132            .compute_root(index.value(), node)
133            .expect("failed to compute Merkle path root")
134    }
135
136    /// Consume the proof and returns its parts.
137    pub fn into_parts(self) -> (MerklePath, Vec<(RpoDigest, Word)>) {
138        (self.path, self.entries)
139    }
140
141    // HELPER METHODS
142    // --------------------------------------------------------------------------------------------
143
144    /// Returns true if the proof is for an empty value.
145    fn is_value_empty(&self) -> bool {
146        self.entries[0].1 == EMPTY_VALUE
147    }
148
149    /// Converts the entries contained in this proof into a node value for node at the base of the
150    /// path contained in this proof.
151    fn build_node(&self) -> RpoDigest {
152        let depth = self.path.depth();
153        if self.is_value_empty() {
154            EmptySubtreeRoots::empty_hashes(MAX_DEPTH)[depth as usize]
155        } else if depth == MAX_DEPTH {
156            hash_bottom_leaf(&self.entries)
157        } else {
158            let (key, value) = self.entries[0];
159            hash_upper_leaf(key, value, depth)
160        }
161    }
162}