miden-objects 0.17.0-rc.4

Canonical Protobuf representations for Miden protocol objects
Documentation
use alloc::string::ToString;
use alloc::vec::Vec;
use alloc::{format, vec};

use miden_protocol::{Felt, Word};

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

#[test]
fn merkle_store_node_verifies() {
    let decoded = proto::primitives::MerkleStoreNode {
        value: Some(Word::empty().into()),
        left: Some(Word::empty().into()),
        right: Some(Word::empty().into()),
    }
    .decode_fields()
    .unwrap();
    assert_eq!(decoded.verify().unwrap().value, Word::empty());
}

#[test]
fn advice_map_entry_verifies() {
    let decoded = proto::primitives::AdviceMapEntry {
        key: Some(Word::empty().into()),
        values: vec![miden_protocol::Felt::ONE.into()],
    }
    .decode_fields()
    .unwrap();
    assert_eq!(decoded.verify().unwrap(), (Word::empty(), vec![miden_protocol::Felt::ONE]));
}

#[test]
fn advice_stack_verifies_in_order() {
    let decoded = proto::primitives::AdviceStack {
        values: vec![miden_protocol::Felt::ONE.into(), miden_protocol::Felt::ZERO.into()],
    }
    .decode_fields()
    .unwrap();
    assert_eq!(
        decoded.verify().unwrap().iter().copied().collect::<Vec<_>>(),
        vec![miden_protocol::Felt::ONE, miden_protocol::Felt::ZERO]
    );
}

#[test]
fn advice_map_verification_rejects_duplicates_after_decoding() {
    let entry = proto::primitives::AdviceMapEntry {
        key: Some(Word::empty().into()),
        values: vec![],
    };
    let decoded = proto::primitives::AdviceMap { entries: vec![entry.clone(), entry] }
        .decode_fields()
        .unwrap();
    assert_eq!(decoded.entries.len(), 2);
    assert!(
        matches!(decoded.verify(), Err(crate::decoded::primitives::AdviceError::DuplicateMapKey(key)) if key == Word::empty())
    );
}

#[test]
fn merkle_store_verification_rejects_duplicates_after_decoding() {
    let node = proto::primitives::MerkleStoreNode {
        value: Some(Word::empty().into()),
        left: Some(Word::empty().into()),
        right: Some(Word::empty().into()),
    };
    let decoded = proto::primitives::MerkleStore { nodes: vec![node.clone(), node] }
        .decode_fields()
        .unwrap();
    assert_eq!(decoded.nodes.len(), 2);
    assert!(
        matches!(decoded.verify(), Err(crate::decoded::primitives::AdviceError::DuplicateMerkleParent(key)) if key == Word::empty())
    );
}

#[test]
fn advice_inputs_decode_nested_records_before_verification() {
    let input = miden_protocol::vm::AdviceInputs::default();
    let decoded = proto::primitives::AdviceInputs::from(&input).decode_fields().unwrap();
    assert!(decoded.advice_map.entries.is_empty());
    assert_eq!(decoded.verify().unwrap(), input);
}

#[test]
fn advice_map_decoding_normalizes_arbitrary_entry_order() {
    let map = proto::primitives::AdviceMap {
        entries: vec![
            proto::primitives::AdviceMapEntry {
                key: Some(dummy_word(7).into()),
                values: vec![Felt::from(3_u32).into()],
            },
            proto::primitives::AdviceMapEntry {
                key: Some(dummy_word(5).into()),
                values: vec![Felt::from(4_u32).into()],
            },
        ],
    }
    .decode_fields()
    .unwrap()
    .verify()
    .unwrap();

    assert_eq!(
        proto::primitives::AdviceMap::from(&map)
            .entries
            .iter()
            .map(|entry| Word::try_from(entry.key.clone().unwrap()).unwrap())
            .collect::<Vec<_>>(),
        vec![dummy_word(5), dummy_word(7)]
    );
}

#[test]
fn advice_map_rejects_duplicate_keys() {
    let duplicate = proto::primitives::AdviceMap {
        entries: vec![
            proto::primitives::AdviceMapEntry {
                key: Some(dummy_word(1).into()),
                values: vec![Felt::from(2_u32).into()],
            },
            proto::primitives::AdviceMapEntry {
                key: Some(dummy_word(1).into()),
                values: vec![Felt::from(3_u32).into()],
            },
        ],
    };
    let error = duplicate
        .decode_fields()
        .unwrap()
        .verify()
        .map_err(ConversionError::new)
        .unwrap_err();
    assert_eq!(error.to_string(), format!("duplicate advice map key {}", dummy_word(1)));
}

#[test]
fn merkle_store_rejects_duplicate_parents() {
    let node = proto::primitives::MerkleStoreNode {
        value: Some(dummy_word(1).into()),
        left: Some(dummy_word(2).into()),
        right: Some(dummy_word(3).into()),
    };
    let duplicate = proto::primitives::MerkleStore { nodes: vec![node.clone(), node] };
    let error = duplicate
        .decode_fields()
        .unwrap()
        .verify()
        .map_err(ConversionError::new)
        .unwrap_err();
    assert_eq!(error.to_string(), format!("duplicate Merkle store parent {}", dummy_word(1)));
}