miden_objects/block/
account_witness.rs

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