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, 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// TIERED SPARSE MERKLE TREE PROOF
 16// ================================================================================================
 17
 18/// A proof which can be used to assert membership (or non-membership) of key-value pairs in a
 19/// Tiered Sparse Merkle tree.
 20///
 21/// The proof consists of a Merkle path and one or more key-value entries which describe the node
 22/// located at the base of the path. If the node at the base of the path resolves to [ZERO; 4],
 23/// the entries will contain a single item with value set to [ZERO; 4].
 24pub struct TieredSmtProof {
 25    path: MerklePath,
 26    entries: Vec<(RpoDigest, Word)>,
 27}
 28
 29impl TieredSmtProof {
 30    // CONSTRUCTOR
 31    // --------------------------------------------------------------------------------------------
 32    /// Returns a new instance of [TieredSmtProof] instantiated from the specified path and entries.
 33    ///
 34    /// # Panics
 35    /// Panics if:
 36    /// - The length of the path is greater than 64.
 37    /// - Entries is an empty vector.
 38    /// - Entries contains more than 1 item, but the length of the path is not 64.
 39    /// - Entries contains more than 1 item, and one of the items has value set to [ZERO; 4].
 40    /// - Entries contains multiple items with keys which don't share the same 64-bit prefix.
 41    pub fn new(path: MerklePath, entries: Vec<(RpoDigest, Word)>) -> Self {
 42        assert!(path.depth() <= MAX_DEPTH);
 43        assert!(!entries.is_empty());
 44        if entries.len() > 1 {
 45            assert!(path.depth() == MAX_DEPTH);
 46            let prefix = get_key_prefix(&entries[0].0);
 47            for entry in entries.iter().skip(1) {
 48                assert_ne!(entry.1, EMPTY_VALUE);
 49                assert_eq!(prefix, get_key_prefix(&entry.0));
 50            }
 51        }
 52
 53        Self { path, entries }
 54    }
 55
 56    // PROOF VERIFIER
 57    // --------------------------------------------------------------------------------------------
 58
 59    /// Returns true if a Tiered Sparse Merkle tree with the specified root contains the provided
 60    /// key-value pair.
 61    ///
 62    /// Note: this method cannot be used to assert non-membership. That is, if false is returned,
 63    /// it does not mean that the provided key-value pair is not in the tree.
 64    pub fn verify_membership(&self, key: &RpoDigest, value: &Word, root: &RpoDigest) -> bool {
 65        if self.is_value_empty() {
 66            if value != &EMPTY_VALUE {
 67                return false;
 68            }
 69            // if the proof is for an empty value, we can verify it against any key which has a
 70            // common prefix with the key storied in entries, but the prefix must be greater than
 71            // the path length
 72            let common_prefix_tier = get_common_prefix_tier_depth(key, &self.entries[0].0);
 73            if common_prefix_tier < self.path.depth() {
 74                return false;
 75            }
 76        } else if !self.entries.contains(&(*key, *value)) {
 77            return false;
 78        }
 79
 80        // make sure the Merkle path resolves to the correct root
 81        root == &self.compute_root()
 82    }
 83
 84    // PUBLIC ACCESSORS
 85    // --------------------------------------------------------------------------------------------
 86
 87    /// Returns the value associated with the specific key according to this proof, or None if
 88    /// this proof does not contain a value for the specified key.
 89    ///
 90    /// A key-value pair generated by using this method should pass the `verify_membership()` check.
 91    pub fn get(&self, key: &RpoDigest) -> Option<Word> {
 92        if self.is_value_empty() {
 93            let common_prefix_tier = get_common_prefix_tier_depth(key, &self.entries[0].0);
 94            if common_prefix_tier < self.path.depth() {
 95                None
 96            } else {
 97                Some(EMPTY_VALUE)
 98            }
 99        } else {
100            self.entries.iter().find(|(k, _)| k == key).map(|(_, value)| *value)
101        }
102    }
103
104    /// Computes the root of a Tiered Sparse Merkle tree to which this proof resolve.
105    pub fn compute_root(&self) -> RpoDigest {
106        let node = self.build_node();
107        let index = LeafNodeIndex::from_key(&self.entries[0].0, self.path.depth());
108        self.path
109            .compute_root(index.value(), node)
110            .expect("failed to compute Merkle path root")
111    }
112
113    // HELPER METHODS
114    // --------------------------------------------------------------------------------------------
115
116    /// Returns true if the proof is for an empty value.
117    fn is_value_empty(&self) -> bool {
118        self.entries[0].1 == EMPTY_VALUE
119    }
120
121    /// Converts the entries contained in this proof into a node value for node at the base of the
122    /// path contained in this proof.
123    fn build_node(&self) -> RpoDigest {
124        let depth = self.path.depth();
125        if self.is_value_empty() {
126            EmptySubtreeRoots::empty_hashes(MAX_DEPTH)[depth as usize]
127        } else if depth == MAX_DEPTH {
128            hash_bottom_leaf(&self.entries)
129        } else {
130            let (key, value) = self.entries[0];
131            hash_upper_leaf(key, value, depth)
132        }
133    }
134}