use miden_crypto::merkle::{InnerNodeInfo, SmtLeaf, SmtProof};
use super::vault_key::AssetVaultKey;
use crate::AssetError;
use crate::asset::Asset;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AssetWitness(SmtProof);
impl AssetWitness {
pub fn new(smt_proof: SmtProof) -> Result<Self, AssetError> {
for (vault_key, asset) in smt_proof.leaf().entries() {
let asset = Asset::try_from(asset)?;
if *vault_key != asset.vault_key().into() {
return Err(AssetError::AssetVaultKeyMismatch {
actual: *vault_key,
expected: asset.vault_key().into(),
});
}
}
Ok(Self(smt_proof))
}
pub fn new_unchecked(smt_proof: SmtProof) -> Self {
Self(smt_proof)
}
pub fn find(&self, vault_key: AssetVaultKey) -> Option<Asset> {
self.assets().find(|asset| asset.vault_key() == vault_key)
}
pub fn assets(&self) -> impl Iterator<Item = Asset> {
let entries = match self.0.leaf() {
SmtLeaf::Empty(_) => &[],
SmtLeaf::Single(kv_pair) => core::slice::from_ref(kv_pair),
SmtLeaf::Multiple(kv_pairs) => kv_pairs,
};
entries.iter().map(|(_key, value)| {
Asset::try_from(value).expect("asset witness should track valid assets")
})
}
pub fn authenticated_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
self.0
.path()
.authenticated_nodes(self.0.leaf().index().value(), self.0.leaf().hash())
.expect("leaf index is u64 and should be less than 2^SMT_DEPTH")
}
}
impl From<AssetWitness> for SmtProof {
fn from(witness: AssetWitness) -> Self {
witness.0
}
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use miden_crypto::merkle::Smt;
use super::*;
use crate::Word;
use crate::asset::{FungibleAsset, NonFungibleAsset};
#[test]
fn create_asset_witness_fails_on_invalid_asset() -> anyhow::Result<()> {
let invalid_asset = Word::from([0, 0, 0, 5u32]);
let smt = Smt::with_entries([(invalid_asset, invalid_asset)])?;
let proof = smt.open(&invalid_asset);
let err = AssetWitness::new(proof).unwrap_err();
assert_matches!(err, AssetError::InvalidFaucetAccountId(_));
Ok(())
}
#[test]
fn create_asset_witness_fails_on_vault_key_mismatch() -> anyhow::Result<()> {
let fungible_asset = FungibleAsset::mock(500);
let non_fungible_asset = NonFungibleAsset::mock(&[1]);
let smt =
Smt::with_entries([(fungible_asset.vault_key().into(), non_fungible_asset.into())])?;
let proof = smt.open(&fungible_asset.vault_key().into());
let err = AssetWitness::new(proof).unwrap_err();
assert_matches!(err, AssetError::AssetVaultKeyMismatch { actual, expected } => {
assert_eq!(actual, fungible_asset.vault_key().into());
assert_eq!(expected, non_fungible_asset.vault_key().into());
});
Ok(())
}
}