miden-stdlib 0.19.1

Miden VM standard library
Documentation
use miden_core::{Word, assert_matches};
use miden_processor::{AdviceInputs, ContextId, DefaultHost, ExecutionError, Program};
use miden_utils_testing::{
    ExecutionOptions, ONE, Process, StackInputs, ZERO, build_expected_hash, build_expected_perm,
    felt_slice_to_ints,
};

#[test]
fn test_memcopy_words() {
    use miden_stdlib::StdLibrary;

    let source = "
    use.std::mem

    begin
        push.0.0.0.1.1000 mem_storew_be dropw
        push.0.0.1.0.1004 mem_storew_be dropw
        push.0.0.1.1.1008 mem_storew_be dropw
        push.0.1.0.0.1012 mem_storew_be dropw
        push.0.1.0.1.1016 mem_storew_be dropw

        push.2000.1000.5 exec.mem::memcopy_words
    end
    ";

    let stdlib = StdLibrary::default();
    let assembler = miden_assembly::Assembler::default()
        .with_dynamic_library(&stdlib)
        .expect("failed to load stdlib");

    let program: Program =
        assembler.assemble_program(source).expect("Failed to compile test source.");

    let mut host = DefaultHost::default().with_library(&stdlib).unwrap();

    let mut process = Process::new(
        program.kernel().clone(),
        StackInputs::default(),
        AdviceInputs::default(),
        ExecutionOptions::default(),
    );
    process.execute(&program, &mut host).unwrap();

    assert_eq!(
        process.chiplets.memory.get_word(ContextId::root(), 1000).unwrap(),
        Some(Word::new([ZERO, ZERO, ZERO, ONE])),
        "Address 1000"
    );
    assert_eq!(
        process.chiplets.memory.get_word(ContextId::root(), 1004).unwrap(),
        Some(Word::new([ZERO, ZERO, ONE, ZERO])),
        "Address 1004"
    );
    assert_eq!(
        process.chiplets.memory.get_word(ContextId::root(), 1008).unwrap(),
        Some(Word::new([ZERO, ZERO, ONE, ONE])),
        "Address 1008"
    );
    assert_eq!(
        process.chiplets.memory.get_word(ContextId::root(), 1012).unwrap(),
        Some(Word::new([ZERO, ONE, ZERO, ZERO])),
        "Address 1012"
    );
    assert_eq!(
        process.chiplets.memory.get_word(ContextId::root(), 1016).unwrap(),
        Some(Word::new([ZERO, ONE, ZERO, ONE])),
        "Address 1016"
    );

    assert_eq!(
        process.chiplets.memory.get_word(ContextId::root(), 2000).unwrap(),
        Some(Word::new([ZERO, ZERO, ZERO, ONE])),
        "Address 2000"
    );
    assert_eq!(
        process.chiplets.memory.get_word(ContextId::root(), 2004).unwrap(),
        Some(Word::new([ZERO, ZERO, ONE, ZERO])),
        "Address 2004"
    );
    assert_eq!(
        process.chiplets.memory.get_word(ContextId::root(), 2008).unwrap(),
        Some(Word::new([ZERO, ZERO, ONE, ONE])),
        "Address 2008"
    );
    assert_eq!(
        process.chiplets.memory.get_word(ContextId::root(), 2012).unwrap(),
        Some(Word::new([ZERO, ONE, ZERO, ZERO])),
        "Address 2012"
    );
    assert_eq!(
        process.chiplets.memory.get_word(ContextId::root(), 2016).unwrap(),
        Some(Word::new([ZERO, ONE, ZERO, ONE])),
        "Address 2016"
    );
}

#[test]
fn test_pipe_double_words_to_memory() {
    let start_addr = 1000;
    let end_addr = 1008;
    let source = format!(
        "
        use.std::mem
        use.std::sys

        begin
            push.{end_addr}
            push.{start_addr}
            padw padw padw  # hasher state

            exec.mem::pipe_double_words_to_memory

            exec.sys::truncate_stack
        end"
    );

    let operand_stack = &[];
    let data = &[1, 2, 3, 4, 5, 6, 7, 8];
    let mut expected_stack =
        felt_slice_to_ints(&build_expected_perm(&[0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]));
    expected_stack.push(end_addr);
    build_test!(source, operand_stack, &data).expect_stack_and_memory(
        &expected_stack,
        start_addr,
        data,
    );
}

#[test]
fn test_pipe_words_to_memory() {
    let mem_addr = 1000;
    let one_word = format!(
        "
        use.std::mem
        use.std::crypto::hashes::rpo

        begin
            push.{mem_addr} # target address
            push.1  # number of words

            exec.mem::pipe_words_to_memory
            exec.rpo::squeeze_digest

            # truncate stack
            swapdw dropw dropw
        end"
    );

    let operand_stack = &[];
    let data = &[1, 2, 3, 4];
    let mut expected_stack = felt_slice_to_ints(&build_expected_hash(data));
    expected_stack.push(1004);
    build_test!(one_word, operand_stack, &data).expect_stack_and_memory(
        &expected_stack,
        mem_addr,
        data,
    );

    let three_words = format!(
        "
        use.std::mem
        use.std::crypto::hashes::rpo

        begin
            push.{mem_addr} # target address
            push.3  # number of words

            exec.mem::pipe_words_to_memory
            exec.rpo::squeeze_digest

            # truncate stack
            swapdw dropw dropw
        end"
    );

    let operand_stack = &[];
    let data = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
    let mut expected_stack = felt_slice_to_ints(&build_expected_hash(data));
    expected_stack.push(1012);
    build_test!(three_words, operand_stack, &data).expect_stack_and_memory(
        &expected_stack,
        mem_addr,
        data,
    );
}

#[test]
fn test_pipe_preimage_to_memory() {
    let mem_addr = 1000;
    let three_words = format!(
        "use.std::mem

        begin
            adv_push.4 # push commitment to stack
            push.{mem_addr}    # target address
            push.3     # number of words

            exec.mem::pipe_preimage_to_memory
            swap drop
        end"
    );

    let operand_stack = &[];
    let data = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
    let mut advice_stack = felt_slice_to_ints(&build_expected_hash(data));
    advice_stack.reverse();
    advice_stack.extend(data);
    build_test!(three_words, operand_stack, &advice_stack).expect_stack_and_memory(
        &[1012],
        mem_addr,
        data,
    );
}

#[test]
fn test_pipe_preimage_to_memory_invalid_preimage() {
    let three_words = "
    use.std::mem

    begin
        adv_push.4  # push commitment to stack
        push.1000   # target address
        push.3      # number of words

        exec.mem::pipe_preimage_to_memory
    end
    ";

    let operand_stack = &[];
    let data = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
    let mut advice_stack = felt_slice_to_ints(&build_expected_hash(data));
    advice_stack.reverse();
    advice_stack[0] += 1; // corrupt the expected hash
    advice_stack.extend(data);
    let res = build_test!(three_words, operand_stack, &advice_stack).execute();
    assert!(res.is_err());
}

#[test]
fn test_pipe_double_words_preimage_to_memory() {
    // Word-aligned address, as required by `pipe_double_words_preimage_to_memory`.
    let mem_addr = 1000;
    let four_words = format!(
        "use.std::mem

        begin
            adv_push.4 # push commitment to stack
            push.{mem_addr}    # target address
            push.4     # number of words

            exec.mem::pipe_double_words_preimage_to_memory
            swap drop
        end"
    );

    let operand_stack = &[];
    let data = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
    let mut advice_stack = felt_slice_to_ints(&build_expected_hash(data));
    advice_stack.reverse();
    advice_stack.extend(data);
    build_test!(four_words, operand_stack, &advice_stack).expect_stack_and_memory(
        &[mem_addr + (4u64 * 4u64)],
        mem_addr as u32,
        data,
    );
}

#[test]
fn test_pipe_double_words_preimage_to_memory_invalid_preimage() {
    let four_words = "
    use.std::mem

    begin
        adv_push.4  # push commitment to stack
        push.1000   # target address
        push.4      # number of words

        exec.mem::pipe_double_words_preimage_to_memory
    end
    ";

    let operand_stack = &[];
    let data = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
    let mut advice_stack = felt_slice_to_ints(&build_expected_hash(data));
    advice_stack.reverse();
    advice_stack[0] += 1; // corrupt the expected hash
    advice_stack.extend(data);
    let execution_result = build_test!(four_words, operand_stack, &advice_stack).execute();
    assert_matches!(execution_result, Err(ExecutionError::FailedAssertion { .. }));
}

#[test]
fn test_pipe_double_words_preimage_to_memory_invalid_count() {
    let three_words = "
    use.std::mem

    begin
        adv_push.4  # push commitment to stack
        push.1000   # target address
        push.3      # number of words

        exec.mem::pipe_double_words_preimage_to_memory
    end
    ";

    let operand_stack = &[];
    let data = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
    let mut advice_stack = felt_slice_to_ints(&build_expected_hash(data));
    advice_stack.reverse();
    advice_stack.extend(data);
    let execution_result = build_test!(three_words, operand_stack, &advice_stack).execute();
    assert_matches!(execution_result, Err(ExecutionError::FailedAssertion { .. }));
}