miden-lib 0.12.4

Standard library of the Miden protocol
Documentation
use.std::crypto::hashes::rpo

use.$kernel::constants
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.
export.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
    exec.constants::get_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: []
export.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.
export.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.
export.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 dup exec.memory::get_output_note_num_assets
        # => [num_assets, note_data_ptr, note_data_ptr]

        # calculate the number of pairs of assets (takes ceiling if we have an odd number)
        add.1
        u32assert.err=ERR_NOTE_NUM_OF_ASSETS_EXCEED_LIMIT
        u32div.2
        # => [num_asset_pairs, note_data_ptr, note_data_ptr]

        # initiate counter for assets
        push.0
        # => [asset_counter, num_asset_pairs, note_data_ptr, note_data_ptr]

        # prepare address and stack for reading assets
        movup.2 exec.memory::get_output_note_asset_data_ptr padw padw padw
        # => [PAD, PAD, PAD, asset_data_ptr, asset_counter, num_asset_pairs, note_data_ptr]

        # check if we should loop
        dup.14 dup.14 neq
        # => [should_loop, PAD, PAD, PAD, asset_data_ptr, asset_counter, num_asset_pairs, note_data_ptr]

        # loop and read assets from memory
        while.true
            # read assets from memory.
            # if this is the last permutation of the loop and we have an odd number of assets then we
            # implicitly pad the last word of the hasher rate with zeros by reading from empty memory.
            mem_stream hperm
            # => [PERM, PERM, PERM, asset_data_ptr, asset_counter, num_asset_pairs, note_data_ptr]

            # check if we should loop again
            movup.13 add.1 dup movdn.14 dup.15 neq
            # => [should_loop, PERM, PERM, PERM, asset_data_ptr, asset_counter, num_asset_pairs,
            #     note_data_ptr]
        end

        # extract digest
        exec.rpo::squeeze_digest
        # => [ASSETS_COMMITMENT, asset_data_ptr, asset_counter, num_asset_pairs, note_data_ptr]

        # drop accessory variables from stack
        movup.4 drop
        movup.4 drop
        movup.4 drop
        # => [ASSETS_COMMITMENT, note_data_ptr]

        # save the assets hash 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), input_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
    # pad capacity elements of hasher
    padw
    # => [EMPTY_WORD, note_data_ptr]

    # insert output note recipient into the first four elements of the hasher rate
    dup.4 exec.memory::get_output_note_recipient
    # => [RECIPIENT, EMPTY_WORD, note_data_ptr]

    # populate the last four elements of the hasher rate with the output note's asset commitment
    dup.8 exec.compute_output_note_assets_commitment
    # => [ASSETS_COMMITMENT, RECIPIENT, EMPTY_WORD, note_data_ptr]

    # compute output note commitment (which is also note ID) and extract digest
    hperm exec.rpo::squeeze_digest
    # => [NOTE_ID, note_data_ptr]

    # save the output note commitment (note ID) to memory
    movup.4 mem_storew_be
    # => [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.
export.compute_output_notes_commitment
    # get the number of output notes from memory
    exec.memory::get_num_output_notes
    # => [num_notes, ...]

    # calculate the address at which we should stop looping
    exec.memory::get_output_note_ptr
    # => [end_ptr, ...]

    # compute pointer for first address
    push.0 exec.memory::get_output_note_ptr
    # => [first_note_ptr, end_ptr, ...]

    # prepare stack for hashing
    padw padw padw
    # => [PERM, PERM, PERM, first_note_ptr, end_ptr, ...]

    # check if the number of output notes is greater then 0. Conditional for the while loop.
    dup.13 dup.13 neq
    # => [PERM, PERM, PERM, first_note_ptr, end_ptr, ...]

    # loop and hash output notes
    while.true
        # compute and save output note ID to memory (this also computes the note's asset commitment)
        dup.12 exec.compute_output_note_id
        # => [NOTE_ID, PERM, PERM, PERM, note_ptr, end_ptr, ...]

        # drop output note ID from stack (it will be read from memory by the next instruction)
        dropw
        # => [PERM, PERM, PERM, note_ptr, end_ptr, ...]

        # permute over (note_id, note_metadata)
        mem_stream hperm
        # => [PERM, PERM, PERM, note_ptr + 8, end_ptr, ...]

        # increment output note pointer
        movup.12 add.OUTPUT_NOTE_HASHING_MEM_DIFF
        # => [note_ptr + 2048, PERM, PERM, PERM, end_ptr, ...]

        # check if we should loop again
        dup movdn.13 dup.14 neq
        # => [should_loop, PERM, PERM, PERM, note_ptr + 512, end_ptr, ...]
    end

    # extract digest
    exec.rpo::squeeze_digest
    # => [OUTPUT_NOTES_COMMITMENT, end_ptr, end_ptr, ...]

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