miden_objects/asset/vault/
partial.rs1use alloc::string::ToString;
2
3use miden_crypto::merkle::{InnerNodeInfo, MerkleError, PartialSmt, SmtLeaf, SmtProof};
4
5use super::AssetVault;
6use crate::Word;
7use crate::asset::Asset;
8use crate::errors::PartialAssetVaultError;
9use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
10
11#[derive(Clone, Debug, PartialEq, Eq, Default)]
17pub struct PartialVault {
18 partial_smt: PartialSmt,
20}
21
22impl PartialVault {
23 pub fn new(partial_smt: PartialSmt) -> Result<Self, PartialAssetVaultError> {
35 Self::validate_entries(partial_smt.entries())?;
36
37 Ok(PartialVault { partial_smt })
38 }
39
40 pub fn root(&self) -> Word {
45 self.partial_smt.root()
46 }
47
48 pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
53 self.partial_smt.inner_nodes()
54 }
55
56 pub fn leaves(&self) -> impl Iterator<Item = &SmtLeaf> {
60 self.partial_smt.leaves().map(|(_, leaf)| leaf)
61 }
62
63 pub fn get(&self, vault_key: Word) -> Result<Option<Asset>, MerkleError> {
72 self.partial_smt.get_value(&vault_key).map(|word| {
73 if word.is_empty() {
74 None
75 } else {
76 Some(Asset::try_from(word).expect("partial vault should only track valid assets"))
79 }
80 })
81 }
82
83 pub fn add(&mut self, proof: SmtProof) -> Result<(), PartialAssetVaultError> {
96 Self::validate_entries(proof.leaf().entries())?;
97
98 self.partial_smt
99 .add_proof(proof)
100 .map_err(PartialAssetVaultError::FailedToAddProof)
101 }
102
103 fn validate_entries<'a>(
111 entries: impl IntoIterator<Item = &'a (Word, Word)>,
112 ) -> Result<(), PartialAssetVaultError> {
113 for (vault_key, asset) in entries {
114 let asset = Asset::try_from(asset).map_err(|source| {
115 PartialAssetVaultError::InvalidAssetInSmt { entry: *asset, source }
116 })?;
117
118 if asset.vault_key() != *vault_key {
119 return Err(PartialAssetVaultError::VaultKeyMismatch {
120 expected: asset.vault_key(),
121 actual: *vault_key,
122 });
123 }
124 }
125
126 Ok(())
127 }
128}
129
130impl From<&AssetVault> for PartialVault {
131 fn from(value: &AssetVault) -> Self {
132 let vault_partial_smt = value.asset_tree.clone().into();
133
134 PartialVault { partial_smt: vault_partial_smt }
135 }
136}
137
138impl Serializable for PartialVault {
139 fn write_into<W: ByteWriter>(&self, target: &mut W) {
140 target.write(&self.partial_smt)
141 }
142}
143
144impl Deserializable for PartialVault {
145 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
146 let vault_partial_smt = source.read()?;
147
148 PartialVault::new(vault_partial_smt)
149 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
150 }
151}
152
153#[cfg(test)]
157mod tests {
158 use assert_matches::assert_matches;
159 use miden_crypto::merkle::Smt;
160
161 use super::*;
162 use crate::asset::FungibleAsset;
163
164 #[test]
165 fn partial_vault_ensures_asset_validity() -> anyhow::Result<()> {
166 let invalid_asset = Word::from([0, 0, 0, 5u32]);
167 let smt = Smt::with_entries([(invalid_asset, invalid_asset)])?;
168 let proof = smt.open(&invalid_asset);
169 let partial_smt = PartialSmt::from_proofs([proof.clone()])?;
170
171 let err = PartialVault::new(partial_smt).unwrap_err();
172 assert_matches!(err, PartialAssetVaultError::InvalidAssetInSmt { entry, .. } => {
173 assert_eq!(entry, invalid_asset);
174 });
175
176 let err = PartialVault::default().add(proof).unwrap_err();
177 assert_matches!(err, PartialAssetVaultError::InvalidAssetInSmt { entry, .. } => {
178 assert_eq!(entry, invalid_asset);
179 });
180
181 Ok(())
182 }
183
184 #[test]
185 fn partial_vault_ensures_asset_vault_key_matches() -> anyhow::Result<()> {
186 let asset = FungibleAsset::mock(500);
187 let invalid_vault_key = Word::from([0, 1, 2, 3u32]);
188 let smt = Smt::with_entries([(invalid_vault_key, asset.into())])?;
189 let proof = smt.open(&invalid_vault_key);
190 let partial_smt = PartialSmt::from_proofs([proof.clone()])?;
191
192 let err = PartialVault::new(partial_smt).unwrap_err();
193 assert_matches!(err, PartialAssetVaultError::VaultKeyMismatch { expected, actual } => {
194 assert_eq!(actual, invalid_vault_key);
195 assert_eq!(expected, asset.vault_key());
196 });
197
198 let err = PartialVault::default().add(proof).unwrap_err();
199 assert_matches!(err, PartialAssetVaultError::VaultKeyMismatch { expected, actual } => {
200 assert_eq!(actual, invalid_vault_key);
201 assert_eq!(expected, asset.vault_key());
202 });
203
204 Ok(())
205 }
206}