miden_crypto/merkle/smt/full/
proof.rs

1use alloc::string::ToString;
2
3use super::{SMT_DEPTH, SmtLeaf, SmtProofError, SparseMerklePath, Word};
4use crate::{
5    merkle::InnerNodeInfo,
6    utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7};
8
9/// A proof which can be used to assert presence (or absence) of key-value pairs
10/// in a [`super::Smt`] (Sparse Merkle Tree).
11///
12/// The proof consists of a sparse Merkle path and a leaf, which describes the node located at
13/// the base of the path.
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct SmtProof {
16    /// The sparse Merkle path from the leaf to the root.
17    path: SparseMerklePath,
18    /// The leaf node containing one or more key-value pairs.
19    leaf: SmtLeaf,
20}
21
22impl SmtProof {
23    // CONSTRUCTOR
24    // --------------------------------------------------------------------------------------------
25
26    /// Returns a new instance of [`SmtProof`] instantiated from the specified path and leaf.
27    ///
28    /// # Errors
29    /// Returns an error if the path length does not match the expected [`SMT_DEPTH`],
30    /// which would make the proof invalid.
31    pub fn new(path: SparseMerklePath, leaf: SmtLeaf) -> Result<Self, SmtProofError> {
32        let depth = path.depth();
33        if depth != SMT_DEPTH {
34            return Err(SmtProofError::InvalidMerklePathLength(depth as usize));
35        }
36
37        Ok(Self { path, leaf })
38    }
39
40    /// Returns a new instance of [`SmtProof`] instantiated from the specified path and leaf.
41    ///
42    /// The length of the path is not checked. Reserved for internal use.
43    pub(in crate::merkle::smt) fn new_unchecked(path: SparseMerklePath, leaf: SmtLeaf) -> Self {
44        Self { path, leaf }
45    }
46
47    // PROOF VERIFIER
48    // --------------------------------------------------------------------------------------------
49
50    /// Verifies that a [`super::Smt`] with the specified root contains the provided key-value pair.
51    ///
52    /// # Errors
53    /// - [`SmtProofError::InvalidKeyForProof`] if the key maps to a different leaf index than this
54    ///   proof's leaf.
55    /// - [`SmtProofError::ConflictingRoots`] if the computed root doesn't match the expected root.
56    /// - [`SmtProofError::ValueMismatch`] if the value doesn't match the value in the leaf.
57    pub fn verify_presence(
58        &self,
59        key: &Word,
60        value: &Word,
61        root: &Word,
62    ) -> Result<(), SmtProofError> {
63        let value_in_leaf = self.leaf.get_value(key).ok_or(SmtProofError::InvalidKeyForProof)?;
64
65        // Check root before value so that ValueMismatch implies the proof is valid
66        let computed_root = self.compute_root();
67        if computed_root != *root {
68            return Err(SmtProofError::ConflictingRoots {
69                expected_root: *root,
70                actual_root: computed_root,
71            });
72        }
73
74        if value_in_leaf != *value {
75            return Err(SmtProofError::ValueMismatch { expected: *value, actual: value_in_leaf });
76        }
77
78        Ok(())
79    }
80
81    /// Verifies that a [`super::Smt`] with the specified root does not contain any value
82    /// for the provided key (i.e., the key is unset).
83    ///
84    /// This is equivalent to calling `verify_presence(key, &EMPTY_WORD, root)`, but makes
85    /// the intent clearer.
86    ///
87    /// # Errors
88    /// - [`SmtProofError::InvalidKeyForProof`] if the key maps to a different leaf index than this
89    ///   proof's leaf.
90    /// - [`SmtProofError::ConflictingRoots`] if the computed root doesn't match the expected root.
91    /// - [`SmtProofError::ValueMismatch`] if the key has a value in the tree (i.e., is not empty).
92    pub fn verify_unset(&self, key: &Word, root: &Word) -> Result<(), SmtProofError> {
93        self.verify_presence(key, &super::EMPTY_WORD, root)
94    }
95
96    /// Verifies that a specific key-value pair is not in the tree.
97    ///
98    /// This succeeds if the key exists with a different value, or if the key is unset.
99    ///
100    /// # Errors
101    /// - [`SmtProofError::InvalidKeyForProof`] if the key maps to a different leaf index than this
102    ///   proof's leaf.
103    /// - [`SmtProofError::ConflictingRoots`] if the computed root doesn't match the expected root.
104    /// - [`SmtProofError::ValuePresent`] if the key-value pair exists in the tree.
105    pub fn verify_absence(
106        &self,
107        key: &Word,
108        value: &Word,
109        root: &Word,
110    ) -> Result<(), SmtProofError> {
111        match self.verify_presence(key, value, root) {
112            // The key-value pair exists - absence verification fails
113            Ok(()) => Err(SmtProofError::ValuePresent { key: *key, value: *value }),
114            // Value is different - the pair is absent, success
115            Err(SmtProofError::ValueMismatch { .. }) => Ok(()),
116            // Other errors propagate as-is
117            Err(e) => Err(e),
118        }
119    }
120
121    // PUBLIC ACCESSORS
122    // --------------------------------------------------------------------------------------------
123
124    /// Returns the value associated with the specific key according to this proof, or None if
125    /// this proof does not contain a value for the specified key.
126    ///
127    /// A key-value pair generated by using this method should pass the `verify_presence()` check.
128    pub fn get(&self, key: &Word) -> Option<Word> {
129        self.leaf.get_value(key)
130    }
131
132    /// Computes the root of a [`super::Smt`] to which this proof resolves.
133    pub fn compute_root(&self) -> Word {
134        self.path
135            .compute_root(self.leaf.index().value(), self.leaf.hash())
136            .expect("failed to compute Merkle path root")
137    }
138
139    /// Returns the proof's sparse Merkle path.
140    pub fn path(&self) -> &SparseMerklePath {
141        &self.path
142    }
143
144    /// Returns the leaf associated with the proof.
145    pub fn leaf(&self) -> &SmtLeaf {
146        &self.leaf
147    }
148
149    /// Returns an iterator over every inner node of this proof's merkle path.
150    pub fn authenticated_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
151        self.path
152            .authenticated_nodes(self.leaf.index().value(), self.leaf.hash())
153            .expect("leaf index is u64 and should be less than 2^SMT_DEPTH")
154    }
155
156    /// Consume the proof and returns its parts.
157    pub fn into_parts(self) -> (SparseMerklePath, SmtLeaf) {
158        (self.path, self.leaf)
159    }
160}
161
162impl Serializable for SmtProof {
163    fn write_into<W: ByteWriter>(&self, target: &mut W) {
164        self.path.write_into(target);
165        self.leaf.write_into(target);
166    }
167}
168
169impl Deserializable for SmtProof {
170    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
171        let path = SparseMerklePath::read_from(source)?;
172        let leaf = SmtLeaf::read_from(source)?;
173
174        Self::new(path, leaf).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
175    }
176}