miden-objects 0.17.0-rc.5

Canonical Protobuf representations for Miden protocol objects
Documentation
use alloc::string::ToString;
use core::error::Error;

use assert_matches::assert_matches;
use miden_protocol::asset::{AssetComposition as ProtocolAssetComposition, FungibleAsset};
use miden_protocol::errors::AssetError;
use miden_protocol::{Felt, Word};

use crate::{ConversionError, DecodeMessage, Verify, proto};

#[test]
fn asset_class_verifies() {
    let decoded = proto::asset::AssetClass {
        suffix: Some(miden_protocol::Felt::ONE.into()),
        prefix: Some(miden_protocol::Felt::ZERO.into()),
    }
    .decode_fields()
    .unwrap();
    assert_eq!(decoded.verify().unwrap().suffix(), miden_protocol::Felt::ONE);
}

#[test]
fn asset_id_composition_payloads_preserve_the_asset_class() {
    use miden_protocol::asset::{AssetClass, AssetComposition, AssetId, FungibleAsset};
    use proto::asset::asset_id::DecodedComposition;

    let faucet_id = FungibleAsset::mock_issuer();
    let nonzero_class =
        AssetClass::new(miden_protocol::Felt::ONE, miden_protocol::Felt::from(2_u32));
    for id in [
        AssetId::new_fungible(faucet_id),
        AssetId::new(AssetClass::default(), faucet_id, AssetComposition::None).unwrap(),
        AssetId::new(nonzero_class, faucet_id, AssetComposition::None).unwrap(),
    ] {
        let decoded = proto::asset::AssetId::from(id).decode_fields().unwrap();
        assert_eq!(decoded.version, proto::asset::AssetVersion::V1);
        match &decoded.composition {
            DecodedComposition::Fungible(()) => assert!(id.asset_class().is_empty()),
            DecodedComposition::NonFungible(class) => {
                assert_eq!(class.suffix, id.asset_class().suffix());
                assert_eq!(class.prefix, id.asset_class().prefix());
            },
            DecodedComposition::Custom(_) => panic!("unexpected custom composition"),
        }
        assert_eq!(decoded.verify().unwrap(), id);
    }
}

#[test]
fn structured_asset_conversion_rejects_custom_composition_after_decoding() {
    let asset_class = proto::asset::AssetClass {
        suffix: Some(Felt::ZERO.into()),
        prefix: Some(Felt::ZERO.into()),
    };
    let faucet_id = Some(FungibleAsset::mock_issuer().into());

    let custom = proto::asset::AssetId {
        version: proto::asset::AssetVersion::V1 as i32,
        composition: Some(proto::asset::asset_id::Composition::Custom(asset_class)),
        faucet_id,
    }
    .decode_fields()
    .unwrap()
    .verify()
    .map_err(ConversionError::new)
    .unwrap_err();
    assert_matches!(
        custom
            .source()
            .and_then(Error::source)
            .and_then(|source| source.downcast_ref::<AssetError>()),
        Some(AssetError::UnsupportedAssetComposition(ProtocolAssetComposition::Custom))
    );
}

#[test]
fn structured_asset_conversion_rejects_invalid_fungible_values() {
    let error = proto::asset::Asset {
        asset_id: Some(proto::asset::AssetId {
            version: proto::asset::AssetVersion::V1 as i32,
            composition: Some(proto::asset::asset_id::Composition::Fungible(())),
            faucet_id: Some(FungibleAsset::mock_issuer().into()),
        }),
        value: Some(Word::from([1_u32, 1, 0, 0]).into()),
    }
    .decode_fields()
    .unwrap()
    .verify()
    .map_err(ConversionError::new)
    .unwrap_err();

    assert_matches!(
        error
            .source()
            .and_then(Error::source)
            .and_then(|source| source.downcast_ref::<AssetError>()),
        Some(AssetError::FungibleAssetValueMostSignificantElementsMustBeZero(_))
    );
}

#[test]
fn asset_id_protobuf_rejects_unspecified_version_after_decoding() {
    let error = proto::asset::AssetId {
        version: proto::asset::AssetVersion::Unspecified as i32,
        composition: Some(proto::asset::asset_id::Composition::Fungible(())),
        faucet_id: Some(FungibleAsset::mock_issuer().into()),
    }
    .decode_fields()
    .unwrap()
    .verify()
    .map_err(ConversionError::new)
    .unwrap_err();

    assert_eq!(error.to_string(), "asset id version is unspecified");
}