miden-protocol 0.14.3

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

# Re-export the max inputs per note constant.
pub use miden::protocol::util::note::MAX_NOTE_STORAGE_ITEMS

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

const ERR_PROLOGUE_NOTE_NUM_STORAGE_ITEMS_EXCEEDED_LIMIT="number of note storage exceeded the maximum limit of 1024"

# NOTE UTILITY PROCEDURES
# =================================================================================================

#! Computes the commitment to the note storage starting at the specified memory address.
#!
#! This procedure checks that the provided number of note storage items is within limits and then computes
#! the commitment.
#!
#! If the number of note storage items is 0, procedure returns the empty word: [0, 0, 0, 0].
#!
#! Inputs:  [storage_ptr, num_storage_items]
#! Outputs: [STORAGE_COMMITMENT]
#!
#! Cycles:
#! - If number of elements divides by 8: 56 cycles + 3 * words
#! - Else: 189 cycles + 3 * words
#!
#! Panics if:
#! - storage_ptr is not word-aligned (i.e., is not a multiple of 4).
#! - num_storage_items is greater than 1024.
#!
#! Invocation: exec
pub proc compute_storage_commitment
    # check that number of storage items is less than or equal to MAX_NOTE_STORAGE_ITEMS
    dup.1 push.MAX_NOTE_STORAGE_ITEMS u32assert2.err=ERR_PROLOGUE_NOTE_NUM_STORAGE_ITEMS_EXCEEDED_LIMIT
    u32lte assert.err=ERR_PROLOGUE_NOTE_NUM_STORAGE_ITEMS_EXCEEDED_LIMIT
    # => [storage_ptr, num_storage_items]

    # compute the storage commitment (over the unpadded values)
    exec.poseidon2::hash_elements
    # => [STORAGE_COMMITMENT]
end

#! Writes the assets data stored in the advice map to the memory specified by the provided
#! destination pointer.
#!
#! Inputs:
#!   Operand stack: [ASSETS_COMMITMENT, num_assets, dest_ptr]
#!   Advice map: {
#!     ASSETS_COMMITMENT: [[ASSETS_DATA]]
#!   }
#! Outputs:
#!   Operand stack: [num_assets, dest_ptr]
pub proc write_assets_to_memory
    # load the asset data from the advice map to the advice stack
    adv.push_mapval
    # OS => [ASSETS_COMMITMENT, num_assets, dest_ptr]
    # AS => [[ASSETS_DATA]]

    dup.5 dup.5
    # OS => [num_assets, dest_ptr, ASSETS_COMMITMENT, num_assets, dest_ptr]
    # AS => [[ASSETS_DATA]]

    # each asset takes up two words, so num_words = 2 * num_assets
    # this also guarantees we pass an even number to pipe_double_words_preimage_to_memory
    mul.2
    # OS => [num_words, dest_ptr, ASSETS_COMMITMENT, num_assets, dest_ptr]
    # AS => [[ASSETS_DATA]]

    # write the data from the advice stack into memory
    exec.mem::pipe_double_words_preimage_to_memory drop
    # OS => [num_assets, dest_ptr]
    # AS => []
end

#! Builds the recipient hash from note storage, script root, and serial number.
#!
#! This procedure computes the commitment of the note storage and then uses it to calculate the note
#! recipient by hashing this commitment, the provided script root, and the serial number.
#!
#! Inputs:
#!   Operand stack: [storage_ptr, num_storage_items, SERIAL_NUM, SCRIPT_ROOT]
#!   Advice map: {
#!      STORAGE_COMMITMENT: [INPUTS],
#!   }
#! Outputs:
#!   Operand stack: [RECIPIENT]
#!   Advice map: {
#!      STORAGE_COMMITMENT: [INPUTS],
#!      RECIPIENT: [SERIAL_SCRIPT_HASH, STORAGE_COMMITMENT],
#!      SERIAL_SCRIPT_HASH: [SERIAL_HASH, SCRIPT_ROOT],
#!      SERIAL_HASH: [SERIAL_NUM, EMPTY_WORD],
#!   }
#!
#! Where:
#! - storage_ptr is the memory address where the note storage are stored.
#! - num_storage_items is the number of input values.
#! - SCRIPT_ROOT is the script root of the note.
#! - SERIAL_NUM is the serial number of the note.
#! - RECIPIENT is the commitment to the input note's script, storage, and the serial number.
#!
#! Locals:
#! - 0: storage_ptr
#! - 1: num_storage_items
#!
#! Panics if:
#! - storage_ptr is not word-aligned (i.e., is not a multiple of 4).
#! - num_storage_items is greater than 1024.
#!
#! Invocation: exec
pub proc build_recipient
    dup.1 dup.1
    # => [storage_ptr, num_storage_items, storage_ptr, num_storage_items, SERIAL_NUM, SCRIPT_ROOT]

    exec.compute_storage_commitment
    # => [STORAGE_COMMITMENT, storage_ptr, num_storage_items, SERIAL_NUM, SCRIPT_ROOT]

    movup.5 movup.5 dup movdn.2
    # => [storage_ptr, num_storage_items, storage_ptr, STORAGE_COMMITMENT, SERIAL_NUM, SCRIPT_ROOT]

    add swap
    # => [storage_ptr, end_ptr, STORAGE_COMMITMENT, SERIAL_NUM, SCRIPT_ROOT]

    movdn.5 movdn.5
    # => [STORAGE_COMMITMENT, storage_ptr, end_ptr, SERIAL_NUM, SCRIPT_ROOT]

    adv.insert_mem
    # => [STORAGE_COMMITMENT, storage_ptr, end_ptr, SERIAL_NUM, SCRIPT_ROOT]

    movup.4 drop movup.4 drop
    # => [STORAGE_COMMITMENT, SERIAL_NUM, SCRIPT_ROOT]

    movdnw.2
    # => [SERIAL_NUM, SCRIPT_ROOT, STORAGE_COMMITMENT]

    padw swapw
    # => [SERIAL_NUM, EMPTY_WORD, SCRIPT_ROOT, STORAGE_COMMITMENT]

    adv.insert_hdword exec.poseidon2::merge
    # => [SERIAL_COMMITMENT, SCRIPT_ROOT, STORAGE_COMMITMENT]

    adv.insert_hdword exec.poseidon2::merge
    # => [SERIAL_SCRIPT_COMMITMENT, STORAGE_COMMITMENT]

    adv.insert_hdword exec.poseidon2::merge
    # => [RECIPIENT]
end

#! Returns the RECIPIENT for a specified SERIAL_NUM, SCRIPT_ROOT and STORAGE_COMMITMENT.
#!
#! Inputs:  [SERIAL_NUM, SCRIPT_ROOT, STORAGE_COMMITMENT]
#! Outputs: [RECIPIENT]
#!
#! Where:
#! - SERIAL_NUM is the serial number of the recipient.
#! - SCRIPT_ROOT is the commitment of the note script.
#! - STORAGE_COMMITMENT is the commitment of the note storage.
#! - RECIPIENT is the recipient of the note.
#!
#! Invocation: exec
pub proc build_recipient_hash
    padw swapw
    # => [SERIAL_NUM, EMPTY_WORD, SCRIPT_ROOT, STORAGE_COMMITMENT]

    exec.poseidon2::merge
    # => [SERIAL_NUM_HASH, SCRIPT_ROOT, STORAGE_COMMITMENT]

    exec.poseidon2::merge
    # => [MERGE_SCRIPT, STORAGE_COMMITMENT]

    exec.poseidon2::merge
    # => [RECIPIENT]
end

#! Extracts the sender ID from the provided metadata header.
#!
#! Inputs:  [METADATA_HEADER]
#! Outputs: [sender_id_suffix, sender_id_prefix]
#!
#! Where:
#! - METADATA_HEADER is the metadata of a note.
#! - sender_{suffix,prefix} are the suffix and prefix felts of the sender ID of the note which
#!   metadata was provided.
pub proc extract_sender_from_metadata
    # => [sender_id_suffix_and_note_type, sender_id_prefix, tag, attachment_kind_scheme]

    # drop tag and attachment_kind_scheme
    movup.3 drop movup.2 drop
    # => [sender_id_suffix_and_note_type, sender_id_prefix]

    # extract suffix of sender from merged layout, which means clearing the least significant byte
    exec.account_id::shape_suffix
    # => [sender_id_suffix, sender_id_prefix]
end

#! Extracts the attachment kind and scheme from the provided metadata header.
#!
#! Inputs:  [METADATA_HEADER]
#! Outputs: [attachment_kind, attachment_scheme]
#!
#! Where:
#! - METADATA_HEADER is the metadata of a note.
#! - attachment_kind is the attachment kind of the note.
#! - attachment_scheme is the attachment scheme of the note.
#!
#! Invocation: exec
pub proc extract_attachment_info_from_metadata
    # => [sender_id_suffix_and_note_type, sender_id_prefix, tag, attachment_kind_scheme]
    drop drop drop
    # => [attachment_kind_scheme]

    # deconstruct the attachment_kind_scheme to extract the attachment_scheme
    # attachment_kind_scheme = [30 zero bits | attachment_kind (2 bits) | attachment_scheme (32 bits)]
    # u32split splits into [lo, hi] where lo is attachment_scheme
    u32split swap
    # => [attachment_kind, attachment_scheme]
end