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