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}