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