miden_objects/asset/vault/
asset_witness.rs1use miden_crypto::merkle::{InnerNodeInfo, SmtLeaf, SmtProof};
2
3use super::vault_key::AssetVaultKey;
4use crate::AssetError;
5use crate::asset::Asset;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct AssetWitness(SmtProof);
12
13impl AssetWitness {
14 pub fn new(smt_proof: SmtProof) -> Result<Self, AssetError> {
25 for (vault_key, asset) in smt_proof.leaf().entries() {
26 let asset = Asset::try_from(asset)?;
27 if *vault_key != asset.vault_key().into() {
28 return Err(AssetError::AssetVaultKeyMismatch {
29 actual: *vault_key,
30 expected: asset.vault_key().into(),
31 });
32 }
33 }
34
35 Ok(Self(smt_proof))
36 }
37
38 pub fn new_unchecked(smt_proof: SmtProof) -> Self {
43 Self(smt_proof)
44 }
45
46 pub fn find(&self, vault_key: AssetVaultKey) -> Option<Asset> {
51 self.assets().find(|asset| asset.vault_key() == vault_key)
52 }
53
54 pub fn assets(&self) -> impl Iterator<Item = Asset> {
56 let entries = match self.0.leaf() {
60 SmtLeaf::Empty(_) => &[],
61 SmtLeaf::Single(kv_pair) => core::slice::from_ref(kv_pair),
62 SmtLeaf::Multiple(kv_pairs) => kv_pairs,
63 };
64
65 entries.iter().map(|(_key, value)| {
66 Asset::try_from(value).expect("asset witness should track valid assets")
67 })
68 }
69
70 pub fn authenticated_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
72 self.0
73 .path()
74 .authenticated_nodes(self.0.leaf().index().value(), self.0.leaf().hash())
75 .expect("leaf index is u64 and should be less than 2^SMT_DEPTH")
76 }
77}
78
79impl From<AssetWitness> for SmtProof {
80 fn from(witness: AssetWitness) -> Self {
81 witness.0
82 }
83}
84
85#[cfg(test)]
89mod tests {
90 use assert_matches::assert_matches;
91 use miden_crypto::merkle::Smt;
92
93 use super::*;
94 use crate::Word;
95 use crate::asset::{FungibleAsset, NonFungibleAsset};
96
97 #[test]
99 fn create_asset_witness_fails_on_invalid_asset() -> anyhow::Result<()> {
100 let invalid_asset = Word::from([0, 0, 0, 5u32]);
101 let smt = Smt::with_entries([(invalid_asset, invalid_asset)])?;
102 let proof = smt.open(&invalid_asset);
103
104 let err = AssetWitness::new(proof).unwrap_err();
105
106 assert_matches!(err, AssetError::InvalidFaucetAccountId(_));
107
108 Ok(())
109 }
110
111 #[test]
114 fn create_asset_witness_fails_on_vault_key_mismatch() -> anyhow::Result<()> {
115 let fungible_asset = FungibleAsset::mock(500);
116 let non_fungible_asset = NonFungibleAsset::mock(&[1]);
117
118 let smt =
119 Smt::with_entries([(fungible_asset.vault_key().into(), non_fungible_asset.into())])?;
120 let proof = smt.open(&fungible_asset.vault_key().into());
121
122 let err = AssetWitness::new(proof).unwrap_err();
123
124 assert_matches!(err, AssetError::AssetVaultKeyMismatch { actual, expected } => {
125 assert_eq!(actual, fungible_asset.vault_key().into());
126 assert_eq!(expected, non_fungible_asset.vault_key().into());
127 });
128
129 Ok(())
130 }
131}