miden_objects/block/
account_witness.rs

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