miden-protocol 0.14.5

Core components of the Miden protocol
Documentation
use miden::core::crypto::hashes::poseidon2

use $kernel::asset::ASSET_SIZE
use $kernel::constants::NOTE_MEM_SIZE
use $kernel::memory

# ERRORS
# =================================================================================================

const ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT="number of assets in a note exceed 255"

# CONSTANTS
# =================================================================================================

# The diff between the memory address after first mem_stream operation and the next target when
# generating the output notes commitment. Must be NOTE_MEM_SIZE - 8;
const OUTPUT_NOTE_HASHING_MEM_DIFF=2040

# ACTIVE NOTE PROCEDURES
# =================================================================================================

#! Move the active input note pointer to the next note and returns the pointer value.
#!
#! Inputs:  []
#! Outputs: [active_input_note_ptr]
#!
#! Where:
#! - active_input_note_ptr is the pointer to the next note to be processed.
pub proc increment_active_input_note_ptr
    # get the active input note pointer
    exec.memory::get_active_input_note_ptr
    # => [orig_input_note_ptr]

    # increment the pointer
    push.NOTE_MEM_SIZE add
    # => [active_input_note_ptr]

    # set the active input note pointer to the incremented value
    dup exec.memory::set_active_input_note_ptr
    # => [active_input_note_ptr]
end

#! Sets the active input note pointer to 0. This should be called after all input notes have
#! been processed.
#!
#! Inputs:  []
#! Outputs: []
pub proc note_processing_teardown
    # set the active input note pointer to 0
    push.0 exec.memory::set_active_input_note_ptr
    # => []
end

#! Prepares a note for execution and pads the stack.
#!
#! Loads the note's script root address and args onto the stack.
#!
#! Inputs:  []
#! Outputs: [note_script_root_ptr, NOTE_ARGS, pad(11)]
#!
#! Where:
#! - note_script_root_ptr is the memory address where note's script root is stored.
#! - NOTE_ARGS is the note's arguments.
pub proc prepare_note
    padw padw push.0.0.0
    # => [pad(11)]

    exec.memory::get_active_input_note_ptr
    # => [note_ptr, pad(11)]

    dup exec.memory::get_input_note_args movup.4
    # => [note_ptr, NOTE_ARGS, pad(11)]

    exec.memory::get_input_note_script_root_ptr
    # => [note_script_root_ptr, NOTE_ARGS, pad(11)]
end

# OUTPUT NOTE PROCEDURES
# =================================================================================================

#! Computes the assets commitment of the output note located at the specified memory address.
#!
#! The hash is computed as a sequential hash of the assets contained in the note. If there is an
#! odd number of assets, then for the final hashing permutation we pad the last word of the hasher
#! rate with zeros. If the note contains no assets, ASSET_COMMITMENT is set to EMPTY_WORD.
#!
#! Recomputation of the assets commitment is performed only if the assets commitment dirty flag is 
#! set to 1. Otherwise the commitment stored in the output note data memory segment is returned.
#!
#! Inputs:  [note_data_ptr]
#! Outputs: [ASSETS_COMMITMENT]
#!
#! Where:
#! - note_data_ptr is a pointer to the data section of the output note.
#! - ASSETS_COMMITMENT is the commitment of the assets of the output note located at note_data_ptr.
pub proc compute_output_note_assets_commitment
    # get the assets commitment dirty flag and decide whether we need to recompute the commitment
    dup exec.memory::get_output_note_dirty_flag
    # => [dirty_flag, note_data_ptr]

    if.true
        # we should recompute assets commitment
        # => [note_data_ptr]

        # duplicate note pointer and fetch num_assets
        dup exec.memory::get_output_note_asset_data_ptr
        # => [assets_ptr, note_data_ptr]

        dup.1 exec.memory::get_output_note_num_assets
        # => [num_assets, assets_ptr, note_data_ptr]

        # compute the asset_end_ptr
        mul.ASSET_SIZE dup.1 add swap
        # => [assets_ptr, assets_end_ptr, note_data_ptr]

        exec.poseidon2::hash_double_words
        # => [ASSETS_COMMITMENT, note_data_ptr]

        # save the assets commitment to memory
        dup.4 exec.memory::set_output_note_assets_commitment
        # => [ASSETS_COMMITMENT, note_data_ptr]

        # update the dirty flag
        push.0 movup.5 exec.memory::set_output_note_dirty_flag
        # => [ASSETS_COMMITMENT]
    else
        # get the cached assets commitment
        exec.memory::get_output_note_assets_commitment
        # => [ASSETS_COMMITMENT]
    end
    # => [ASSETS_COMMITMENT]
end

#! Computes the ID of an output note located at the specified memory address.
#!
#! The note ID is computed as follows:
#! - we define, recipient =
#!       hash(hash(hash(serial_num, [0; 4]), script_root), storage_commitment)
#! - we then compute the output note ID as:
#!       hash(recipient, assets_commitment)
#!
#! Inputs:  [note_data_ptr]
#! Outputs: [NOTE_ID]
#!
#! Where:
#! - note_data_ptr is a pointer to the data section of the output note.
#! - NOTE_ID is the ID of the output note located at note_data_ptr.
proc compute_output_note_id
    # compute assets commitment
    dup exec.compute_output_note_assets_commitment
    # => [ASSETS_COMMITMENT, note_data_ptr]

    dup.4 exec.memory::get_output_note_recipient
    # => [RECIPIENT, ASSETS_COMMITMENT, note_data_ptr]

    # compute output note ID
    exec.poseidon2::merge
    # => [NOTE_ID, note_data_ptr]

    # save the output note commitment (note ID) to memory
    movup.4 mem_storew_le
    # => [NOTE_ID]
end

#! Computes a commitment to the output notes. This is computed as a sequential hash of
#! (note_id, note_metadata) tuples.
#!
#! Inputs:  []
#! Outputs: [OUTPUT_NOTES_COMMITMENT]
#!
#! Where:
#! - OUTPUT_NOTES_COMMITMENT is the commitment to the notes output by the transaction.
pub proc compute_output_notes_commitment
    # get the number of output notes from memory
    exec.memory::get_num_output_notes
    # => [num_notes]

    # initialize the output note index at which to start the commitment computation
    push.0
    # => [current_index = 0, num_notes]

    # prepare stack for hashing
    exec.poseidon2::init_no_padding
    # => [RATE0, RATE1, CAPACITY, current_index, num_notes]

    # starting looping if num_notes != 0
    dup.13 neq.0
    # => [should_loop, RATE0, RATE1, CAPACITY, current_index, num_notes]

    # loop and hash output notes
    while.true
        dup.12 exec.memory::get_output_note_ptr
        # => [current_note_ptr, RATE0, RATE1, CAPACITY, current_index, num_notes]

        # compute and save output note ID to memory (this also computes the note's asset commitment)
        dup exec.compute_output_note_id
        # => [NOTE_ID, current_note_ptr, RATE0, RATE1, CAPACITY, current_index, num_notes]

        dup.4 exec.memory::get_output_note_attachment
        # => [NOTE_ATTACHMENT, NOTE_ID, current_note_ptr, RATE0, RATE1, CAPACITY, current_index, num_notes]

        movup.8 exec.memory::get_output_note_metadata_header
        # => [NOTE_METADATA_HEADER, NOTE_ATTACHMENT, NOTE_ID, RATE0, RATE1, CAPACITY, current_index, num_notes]

        # compute hash(NOTE_METADATA_HEADER || NOTE_ATTACHMENT)
        exec.poseidon2::merge
        # => [NOTE_METADATA_COMMITMENT, NOTE_ID, RATE0, RATE1, CAPACITY, current_index, num_notes]

        # replace rate words with note ID and metadata commitment
        swapdw dropw dropw
        # => [NOTE_METADATA_COMMITMENT, NOTE_ID, CAPACITY, current_index, num_notes]

        # move note ID to the top of the stack
        swapw
        # => [NOTE_ID, NOTE_METADATA_COMMITMENT, CAPACITY, current_index, num_notes]

        # permute over (NOTE_ID, NOTE_METADATA_COMMITMENT)
        exec.poseidon2::permute
        # => [RATE0, RATE1, CAPACITY, current_index, num_notes]

        # increment current_index
        movup.12 add.1 movdn.12
        # => [RATE0, RATE1, CAPACITY, current_index + 1, num_notes]

        # continue looping if current_index != num_notes
        dup.13 dup.13 neq
        # => [should_loop, RATE0, RATE1, CAPACITY, current_index + 1, num_notes]
    end
    # => [RATE0, RATE1, CAPACITY, current_index + 1, num_notes]

    # extract digest
    exec.poseidon2::squeeze_digest
    # => [OUTPUT_NOTES_COMMITMENT, current_index + 1, num_notes]

    # drop accessory variables from stack
    movup.4 drop
    movup.4 drop
    # => [OUTPUT_NOTES_COMMITMENT]
end