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