miden_protocol/asset/vault/
mod.rs1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use miden_crypto::merkle::InnerNodeInfo;
5
6use super::{
7 AccountType,
8 Asset,
9 ByteReader,
10 ByteWriter,
11 Deserializable,
12 DeserializationError,
13 FungibleAsset,
14 NonFungibleAsset,
15 Serializable,
16};
17use crate::Word;
18use crate::account::{AccountId, AccountVaultDelta, NonFungibleDeltaAction};
19use crate::crypto::merkle::smt::{SMT_DEPTH, Smt};
20use crate::errors::AssetVaultError;
21
22mod partial;
23pub use partial::PartialVault;
24
25mod asset_witness;
26pub use asset_witness::AssetWitness;
27
28mod vault_key;
29pub use vault_key::AssetVaultKey;
30
31mod asset_id;
32pub use asset_id::AssetId;
33
34#[derive(Debug, Clone, Default, PartialEq, Eq)]
49pub struct AssetVault {
50 asset_tree: Smt,
51}
52
53impl AssetVault {
54 pub const DEPTH: u8 = SMT_DEPTH;
59
60 pub fn new(assets: &[Asset]) -> Result<Self, AssetVaultError> {
65 Ok(Self {
66 asset_tree: Smt::with_entries(
67 assets.iter().map(|asset| (asset.vault_key().to_word(), asset.to_value_word())),
68 )
69 .map_err(AssetVaultError::DuplicateAsset)?,
70 })
71 }
72
73 pub fn root(&self) -> Word {
78 self.asset_tree.root()
79 }
80
81 pub fn get(&self, asset_vault_key: AssetVaultKey) -> Option<Asset> {
84 let asset_value = self.asset_tree.get_value(&asset_vault_key.to_word());
85
86 if asset_value.is_empty() {
87 None
88 } else {
89 Some(
90 Asset::from_key_value(asset_vault_key, asset_value)
91 .expect("asset vault should only store valid assets"),
92 )
93 }
94 }
95
96 pub fn has_non_fungible_asset(&self, asset: NonFungibleAsset) -> Result<bool, AssetVaultError> {
98 match self.asset_tree.get_value(&asset.vault_key().to_word()) {
100 asset if asset == Smt::EMPTY_VALUE => Ok(false),
101 _ => Ok(true),
102 }
103 }
104
105 pub fn get_balance(&self, faucet_id: AccountId) -> Result<u64, AssetVaultError> {
111 if !matches!(faucet_id.account_type(), AccountType::FungibleFaucet) {
112 return Err(AssetVaultError::NotAFungibleFaucetId(faucet_id));
113 }
114
115 let vault_key =
116 AssetVaultKey::new_fungible(faucet_id).expect("faucet ID should be of type fungible");
117 let asset_value = self.asset_tree.get_value(&vault_key.to_word());
118 let asset = FungibleAsset::from_key_value(vault_key, asset_value)
119 .expect("asset vault should only store valid assets");
120
121 Ok(asset.amount())
122 }
123
124 pub fn assets(&self) -> impl Iterator<Item = Asset> + '_ {
126 self.asset_tree.entries().map(|(key, value)| {
128 Asset::from_key_value_words(*key, *value)
129 .expect("asset vault should only store valid assets")
130 })
131 }
132
133 pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
135 self.asset_tree.inner_nodes()
136 }
137
138 pub fn open(&self, vault_key: AssetVaultKey) -> AssetWitness {
142 let smt_proof = self.asset_tree.open(&vault_key.to_word());
143 AssetWitness::new_unchecked(smt_proof)
145 }
146
147 pub fn is_empty(&self) -> bool {
149 self.asset_tree.is_empty()
150 }
151
152 pub fn num_leaves(&self) -> usize {
157 self.asset_tree.num_leaves()
158 }
159
160 pub fn num_assets(&self) -> usize {
165 self.asset_tree.num_entries()
166 }
167
168 pub fn apply_delta(&mut self, delta: &AccountVaultDelta) -> Result<(), AssetVaultError> {
182 for (&faucet_id, &delta) in delta.fungible().iter() {
183 let asset = FungibleAsset::new(faucet_id, delta.unsigned_abs())
184 .expect("Not a fungible faucet ID or delta is too large");
185 match delta >= 0 {
186 true => self.add_fungible_asset(asset),
187 false => self.remove_fungible_asset(asset),
188 }?;
189 }
190
191 for (&asset, &action) in delta.non_fungible().iter() {
192 match action {
193 NonFungibleDeltaAction::Add => self.add_non_fungible_asset(asset),
194 NonFungibleDeltaAction::Remove => self.remove_non_fungible_asset(asset),
195 }?;
196 }
197
198 Ok(())
199 }
200
201 pub fn add_asset(&mut self, asset: Asset) -> Result<Asset, AssetVaultError> {
210 Ok(match asset {
211 Asset::Fungible(asset) => Asset::Fungible(self.add_fungible_asset(asset)?),
212 Asset::NonFungible(asset) => Asset::NonFungible(self.add_non_fungible_asset(asset)?),
213 })
214 }
215
216 fn add_fungible_asset(
223 &mut self,
224 other_asset: FungibleAsset,
225 ) -> Result<FungibleAsset, AssetVaultError> {
226 let current_asset_value = self.asset_tree.get_value(&other_asset.vault_key().to_word());
227 let current_asset =
228 FungibleAsset::from_key_value(other_asset.vault_key(), current_asset_value)
229 .expect("asset vault should store valid assets");
230
231 let new_asset = current_asset
232 .add(other_asset)
233 .map_err(AssetVaultError::AddFungibleAssetBalanceError)?;
234
235 self.asset_tree
236 .insert(new_asset.vault_key().to_word(), new_asset.to_value_word())
237 .map_err(AssetVaultError::MaxLeafEntriesExceeded)?;
238
239 Ok(new_asset)
240 }
241
242 fn add_non_fungible_asset(
248 &mut self,
249 asset: NonFungibleAsset,
250 ) -> Result<NonFungibleAsset, AssetVaultError> {
251 let old = self
253 .asset_tree
254 .insert(asset.vault_key().to_word(), asset.to_value_word())
255 .map_err(AssetVaultError::MaxLeafEntriesExceeded)?;
256
257 if old != Smt::EMPTY_VALUE {
259 return Err(AssetVaultError::DuplicateNonFungibleAsset(asset));
260 }
261
262 Ok(asset)
263 }
264
265 pub fn remove_asset(&mut self, asset: Asset) -> Result<Asset, AssetVaultError> {
274 match asset {
275 Asset::Fungible(asset) => {
276 let asset = self.remove_fungible_asset(asset)?;
277 Ok(Asset::Fungible(asset))
278 },
279 Asset::NonFungible(asset) => {
280 let asset = self.remove_non_fungible_asset(asset)?;
281 Ok(Asset::NonFungible(asset))
282 },
283 }
284 }
285
286 fn remove_fungible_asset(
294 &mut self,
295 other_asset: FungibleAsset,
296 ) -> Result<FungibleAsset, AssetVaultError> {
297 let current_asset_value = self.asset_tree.get_value(&other_asset.vault_key().to_word());
298 let current_asset =
299 FungibleAsset::from_key_value(other_asset.vault_key(), current_asset_value)
300 .expect("asset vault should store valid assets");
301
302 if current_asset.amount() == 0 {
304 return Err(AssetVaultError::FungibleAssetNotFound(other_asset));
305 }
306
307 let new_asset = current_asset
308 .sub(other_asset)
309 .map_err(AssetVaultError::SubtractFungibleAssetBalanceError)?;
310
311 #[cfg(debug_assertions)]
315 {
316 if new_asset.amount() == 0 {
317 assert!(new_asset.to_value_word().is_empty())
318 }
319 }
320
321 self.asset_tree
322 .insert(new_asset.vault_key().to_word(), new_asset.to_value_word())
323 .map_err(AssetVaultError::MaxLeafEntriesExceeded)?;
324
325 Ok(other_asset)
326 }
327
328 fn remove_non_fungible_asset(
335 &mut self,
336 asset: NonFungibleAsset,
337 ) -> Result<NonFungibleAsset, AssetVaultError> {
338 let old = self
340 .asset_tree
341 .insert(asset.vault_key().to_word(), Smt::EMPTY_VALUE)
342 .map_err(AssetVaultError::MaxLeafEntriesExceeded)?;
343
344 if old == Smt::EMPTY_VALUE {
346 return Err(AssetVaultError::NonFungibleAssetNotFound(asset));
347 }
348
349 Ok(asset)
351 }
352}
353
354impl Serializable for AssetVault {
358 fn write_into<W: ByteWriter>(&self, target: &mut W) {
359 let num_assets = self.asset_tree.num_entries();
360 target.write_usize(num_assets);
361 target.write_many(self.assets());
362 }
363
364 fn get_size_hint(&self) -> usize {
365 let mut size = 0;
366 let mut count: usize = 0;
367
368 for asset in self.assets() {
369 size += asset.get_size_hint();
370 count += 1;
371 }
372
373 size += count.get_size_hint();
374
375 size
376 }
377}
378
379impl Deserializable for AssetVault {
380 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
381 let num_assets = source.read_usize()?;
382 let assets = source.read_many_iter::<Asset>(num_assets)?.collect::<Result<Vec<_>, _>>()?;
383 Self::new(&assets).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
384 }
385}
386
387#[cfg(test)]
391mod tests {
392 use assert_matches::assert_matches;
393
394 use super::*;
395
396 #[test]
397 fn vault_fails_on_absent_fungible_asset() {
398 let mut vault = AssetVault::default();
399 let err = vault.remove_asset(FungibleAsset::mock(50)).unwrap_err();
400 assert_matches!(err, AssetVaultError::FungibleAssetNotFound(_));
401 }
402}