miden_protocol/block/account_tree/
witness.rs

1use alloc::string::ToString;
2
3use miden_crypto::merkle::smt::{LeafIndex, SMT_DEPTH, SmtLeaf, SmtProof, SmtProofError};
4use miden_crypto::merkle::{InnerNodeInfo, SparseMerklePath};
5
6use crate::Word;
7use crate::account::AccountId;
8use crate::block::account_tree::{account_id_to_smt_key, smt_key_to_account_id};
9use crate::errors::AccountTreeError;
10use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
11
12// ACCOUNT WITNESS
13// ================================================================================================
14
15/// A specialized version of an [`SmtProof`] for use in [`AccountTree`](super::AccountTree) and
16/// [`PartialAccountTree`](super::PartialAccountTree). It proves the inclusion of an account
17/// ID at a certain state (i.e. [`Account::commitment`](crate::account::Account::commitment)) in the
18/// [`AccountTree`](super::AccountTree).
19///
20/// By construction the witness can only represent the equivalent of an [`SmtLeaf`] with zero or one
21/// entries, which guarantees that the account ID prefix it represents is unique in the tree.
22///
23/// # Guarantees
24///
25/// This type guarantees that:
26/// - its MerklePath is of depth [`SMT_DEPTH`].
27/// - converting this type into an [`SmtProof`] results in a leaf with zero or one entries, i.e. the
28///   account ID prefix is unique.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct AccountWitness {
31    /// The account ID that this witness proves inclusion for.
32    id: AccountId,
33    /// The state commitment of the account ID.
34    commitment: Word,
35    /// The merkle path of the account witness.
36    path: SparseMerklePath,
37}
38
39impl AccountWitness {
40    /// Constructs a new [`AccountWitness`] from the provided parts.
41    ///
42    /// # Errors
43    ///
44    /// Returns an error if:
45    /// - the merkle path's depth is not [`SMT_DEPTH`].
46    pub fn new(
47        account_id: AccountId,
48        commitment: Word,
49        path: SparseMerklePath,
50    ) -> Result<Self, AccountTreeError> {
51        if path.depth() != SMT_DEPTH {
52            return Err(AccountTreeError::WitnessMerklePathDepthDoesNotMatchAccountTreeDepth(
53                path.depth() as usize,
54            ));
55        }
56
57        Ok(Self::new_unchecked(account_id, commitment, path))
58    }
59
60    /// Creates an [`AccountWitness`] from the provided proof and the account ID for which the proof
61    /// was requested.
62    ///
63    /// # Warning
64    ///
65    /// This should only be called on SMT proofs retrieved from (partial) account tree, because it
66    /// relies on the guarantees of those types.
67    ///
68    /// # Panics
69    ///
70    /// Panics if:
71    /// - the merkle path in the proof does not have depth equal to [`SMT_DEPTH`].
72    /// - the proof contains an SmtLeaf::Multiple.
73    pub(super) fn from_smt_proof(requested_account_id: AccountId, proof: SmtProof) -> Self {
74        // Check which account ID this proof actually contains. We rely on the fact that the
75        // trees only contain zero or one entry per account ID prefix.
76        //
77        // If the requested account ID matches an existing ID's prefix but their suffixes do
78        // not match, then this witness is for the _existing ID_.
79        //
80        // Otherwise, if the ID matches the one in the leaf or if it's empty, the witness is
81        // for the requested ID.
82        let witness_id = match proof.leaf() {
83            SmtLeaf::Empty(_) => requested_account_id,
84            SmtLeaf::Single((key_in_leaf, _)) => {
85                // SAFETY: By construction, the tree only contains valid IDs.
86                smt_key_to_account_id(*key_in_leaf)
87            },
88            SmtLeaf::Multiple(_) => {
89                unreachable!("account tree should only contain zero or one entry per ID prefix")
90            },
91        };
92
93        let commitment = proof
94            .get(&account_id_to_smt_key(witness_id))
95            .expect("we should have received a proof for the witness key");
96
97        // SAFETY: The proof is guaranteed to have depth SMT_DEPTH if it comes from one of
98        // the account trees.
99        debug_assert_eq!(proof.path().depth(), SMT_DEPTH);
100
101        AccountWitness::new_unchecked(witness_id, commitment, proof.into_parts().0)
102    }
103
104    /// Constructs a new [`AccountWitness`] from the provided parts.
105    ///
106    /// # Warning
107    ///
108    /// This does not validate any of the guarantees of this type.
109    pub(super) fn new_unchecked(
110        account_id: AccountId,
111        commitment: Word,
112        path: SparseMerklePath,
113    ) -> Self {
114        Self { id: account_id, commitment, path }
115    }
116
117    /// Returns the underlying [`AccountId`] that this witness proves inclusion for.
118    pub fn id(&self) -> AccountId {
119        self.id
120    }
121
122    /// Returns the state commitment of the account witness.
123    pub fn state_commitment(&self) -> Word {
124        self.commitment
125    }
126
127    /// Returns the [`SparseMerklePath`] of the account witness.
128    pub fn path(&self) -> &SparseMerklePath {
129        &self.path
130    }
131
132    /// Returns the [`SmtLeaf`] of the account witness.
133    pub fn leaf(&self) -> SmtLeaf {
134        if self.commitment == Word::empty() {
135            let leaf_idx = LeafIndex::from(account_id_to_smt_key(self.id));
136            SmtLeaf::new_empty(leaf_idx)
137        } else {
138            let key = account_id_to_smt_key(self.id);
139            SmtLeaf::new_single(key, self.commitment)
140        }
141    }
142
143    /// Consumes self and returns the inner proof.
144    pub fn into_proof(self) -> SmtProof {
145        let leaf = self.leaf();
146        debug_assert_eq!(self.path.depth(), SMT_DEPTH);
147        SmtProof::new(self.path, leaf)
148            .expect("merkle path depth should be the SMT depth by construction")
149    }
150
151    /// Returns an iterator over every inner node of this witness' merkle path.
152    pub fn authenticated_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
153        let leaf = self.leaf();
154        self.path()
155            .authenticated_nodes(leaf.index().value(), leaf.hash())
156            .expect("leaf index is u64 and should be less than 2^SMT_DEPTH")
157    }
158}
159
160impl From<AccountWitness> for SmtProof {
161    fn from(witness: AccountWitness) -> Self {
162        witness.into_proof()
163    }
164}
165
166// SERIALIZATION
167// ================================================================================================
168
169impl Serializable for AccountWitness {
170    fn write_into<W: ByteWriter>(&self, target: &mut W) {
171        self.id.write_into(target);
172        self.commitment.write_into(target);
173        self.path.write_into(target);
174    }
175}
176
177impl Deserializable for AccountWitness {
178    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
179        let id = AccountId::read_from(source)?;
180        let commitment = Word::read_from(source)?;
181        let path = SparseMerklePath::read_from(source)?;
182
183        if path.depth() != SMT_DEPTH {
184            return Err(DeserializationError::InvalidValue(
185                SmtProofError::InvalidMerklePathLength(path.depth() as usize).to_string(),
186            ));
187        }
188
189        Ok(Self { id, commitment, path })
190    }
191}