use miden::core::crypto::hashes::poseidon2
use miden::core::mem
use miden::protocol::kernel_proc_offsets::INPUT_NOTE_GET_ASSETS_INFO_OFFSET
use miden::protocol::kernel_proc_offsets::INPUT_NOTE_GET_RECIPIENT_OFFSET
use miden::protocol::kernel_proc_offsets::INPUT_NOTE_GET_STORAGE_INFO_OFFSET
use miden::protocol::kernel_proc_offsets::INPUT_NOTE_GET_METADATA_OFFSET
use miden::protocol::kernel_proc_offsets::INPUT_NOTE_GET_SERIAL_NUMBER_OFFSET
use miden::protocol::kernel_proc_offsets::INPUT_NOTE_GET_SCRIPT_ROOT_OFFSET
use miden::protocol::note
use miden::protocol::input_note
use miden::protocol::util::note::NOTE_TYPE_PUBLIC
use miden::protocol::util::note::NOTE_TYPE_PRIVATE
# ERRORS
# =================================================================================================
const ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT="note data does not match the commitment"
const ERR_NOTE_INVALID_NUMBER_OF_STORAGE_ITEMS="the specified number of note storage items does not match the actual number"
# ACTIVE NOTE PROCEDURES
# =================================================================================================
#
# By the "active note" notion here we assume the note which is currently being processed by the
# transaction kernel.
#! Writes the assets of the active note into memory starting at the specified address.
#!
#! Inputs: [dest_ptr]
#! Outputs: [num_assets]
#!
#! Where:
#! - dest_ptr is the memory address to write the assets.
#! - num_assets is the number of assets in the active note.
#!
#! Panics if:
#! - no note is currently active.
#!
#! Invocation: exec
pub proc get_assets
# pad the stack
padw padw padw push.0.0
# => [pad(14), dest_ptr]
# push the flag indicating that we want to request assets info from the active note
push.1
# => [is_active_note = 1, pad(14), dest_ptr]
push.INPUT_NOTE_GET_ASSETS_INFO_OFFSET
# => [offset, is_active_note = 1, pad(14), dest_ptr]
syscall.exec_kernel_proc
# => [ASSETS_COMMITMENT, num_assets, pad(11), dest_ptr]
# clean the stack
swapdw dropw dropw movup.7 movup.7 movup.7 drop drop drop
# => [ASSETS_COMMITMENT, num_assets, dest_ptr]
# save num_assets for the return value
dup.4 movdn.6
# => [ASSETS_COMMITMENT, num_assets, dest_ptr, num_assets]
# write the assets from the advice map into memory
exec.note::write_assets_to_memory
# => [num_assets]
end
#! Returns the recipient of the active note.
#!
#! Inputs: []
#! Outputs: [RECIPIENT]
#!
#! Where:
#! - RECIPIENT is the commitment to the active note's script, storage, the serial number.
#!
#! Panics if:
#! - no note is currently active.
#!
#! Invocation: exec
pub proc get_recipient
# pad the stack
padw padw padw push.0.0
# => [pad(14)]
# push the flag indicating that we want to request recipient from the active note
push.1
# => [is_active_note = 1, pad(14)]
push.INPUT_NOTE_GET_RECIPIENT_OFFSET
# => [offset, is_active_note = 1, pad(14)]
syscall.exec_kernel_proc
# => [RECIPIENT, pad(12)]
# clean the stack
swapdw dropw dropw swapw dropw
# => [RECIPIENT]
end
#! Writes the active note's storage to memory starting at the specified address.
#!
#! Inputs:
#! Stack: [dest_ptr]
#! Advice Map: { NOTE_STORAGE_COMMITMENT: [STORAGE] }
#! Outputs:
#! Stack: [num_storage_items]
#!
#! Where:
#! - dest_ptr is the memory address to write the note storage.
#! - NOTE_STORAGE_COMMITMENT is the commitment to the note's storage.
#! - STORAGE is the data corresponding to the note's storage.
#!
#! Panics if:
#! - no note is currently active.
#!
#! Invocation: exec
pub proc get_storage
# pad the stack
padw padw padw push.0.0
# => [pad(14), dest_ptr]
# push the flag indicating that we want to request inputs info from the active note
push.1
# => [is_active_note = 1, pad(14), dest_ptr]
push.INPUT_NOTE_GET_STORAGE_INFO_OFFSET
# => [offset, is_active_note = 1, pad(14), dest_ptr]
syscall.exec_kernel_proc
# => [NOTE_STORAGE_COMMITMENT, num_storage_items, pad(11), dest_ptr]
# clean the stack
swapdw dropw dropw
movup.5 drop movup.5 drop movup.5 drop
# => [NOTE_STORAGE_COMMITMENT, num_storage_items, dest_ptr]
# save num_storage_items for the return value
dup.4 movdn.6
# => [NOTE_STORAGE_COMMITMENT, num_storage_items, dest_ptr, num_storage_items]
# write the inputs to the provided destination pointer
exec.write_storage_to_memory
# => [num_storage_items]
end
#! Returns the metadata of the active note.
#!
#! Inputs: []
#! Outputs: [METADATA]
#!
#! Where:
#! - METADATA is the metadata of the active note.
#!
#! Panics if:
#! - no note is currently active.
#!
#! Invocation: exec
pub proc get_metadata
# pad the stack
padw padw padw push.0.0
# => [pad(14)]
# push the flag indicating that we want to request metadata from the active note
push.1
# => [is_active_note = 1, pad(14)]
push.INPUT_NOTE_GET_METADATA_OFFSET
# => [offset, is_active_note = 1, pad(14)]
syscall.exec_kernel_proc
# => [METADATA, pad(12)]
# clean the stack
swapdw dropw dropw swapw dropw
# => [METADATA]
end
#! Returns whether the active note is public.
#!
#! Inputs: []
#! Outputs: [is_public]
#!
#! Where:
#! - is_public is 1 if the active note is public, 0 otherwise.
#!
#! Panics if:
#! - no note is currently active.
#!
#! Invocation: exec
pub proc is_public
exec.get_metadata
# => [METADATA]
exec.note::metadata_into_note_type
# => [note_type]
eq.NOTE_TYPE_PUBLIC
# => [is_public]
end
#! Returns whether the active note is private.
#!
#! Inputs: []
#! Outputs: [is_private]
#!
#! Where:
#! - is_private is 1 if the active note is private, 0 otherwise.
#!
#! Panics if:
#! - no note is currently active.
#!
#! Invocation: exec
pub proc is_private
exec.get_metadata
# => [METADATA]
exec.note::metadata_into_note_type
# => [note_type]
eq.NOTE_TYPE_PRIVATE
# => [is_private]
end
#! Returns the sender of the active note.
#!
#! Inputs: []
#! Outputs: [sender_id_suffix, sender_id_prefix]
#!
#! Where:
#! - sender_{suffix,prefix} are the suffix and prefix felts of the sender of the active note.
#!
#! Panics if:
#! - no note is currently active.
#!
#! Invocation: exec
pub proc get_sender
exec.get_metadata
# => [METADATA]
# extract the sender ID from the metadata
exec.note::metadata_into_sender
# => [sender_id_suffix, sender_id_prefix]
end
#! Returns the serial number of the active note.
#!
#! Inputs: []
#! Outputs: [SERIAL_NUMBER]
#!
#! Where:
#! - SERIAL_NUMBER is the serial number of the active note.
#!
#! Panics if:
#! - no note is currently active.
#!
#! Invocation: exec
pub proc get_serial_number
# pad the stack
padw padw padw push.0.0
# => [pad(14)]
# push the flag indicating that we want to request serial number from the active note
push.1
# => [is_active_note = 1, pad(14)]
push.INPUT_NOTE_GET_SERIAL_NUMBER_OFFSET
# => [offset, is_active_note = 1, pad(14)]
syscall.exec_kernel_proc
# => [SERIAL_NUMBER, pad(12)]
# clean the stack
swapdw dropw dropw swapw dropw
# => [SERIAL_NUMBER]
end
#! Returns the script root of the active note.
#!
#! Inputs: []
#! Outputs: [SCRIPT_ROOT]
#!
#! Where:
#! - SCRIPT_ROOT is the script root of the active note.
#!
#! Panics if:
#! - no note is currently active.
#!
#! Invocation: exec
pub proc get_script_root
# pad the stack
padw padw padw push.0.0
# => [pad(14)]
# push the flag indicating that we want to request script root from the active note
push.1
# => [is_active_note = 1, pad(14)]
push.INPUT_NOTE_GET_SCRIPT_ROOT_OFFSET
# => [offset, is_active_note = 1, pad(14)]
syscall.exec_kernel_proc
# => [SCRIPT_ROOT, pad(12)]
# clean the stack
swapdw dropw dropw swapw dropw
# => [SCRIPT_ROOT]
end
# HELPER PROCEDURES
# =================================================================================================
#! Writes the note storage stored in the advice map to the memory specified by the provided
#! destination pointer.
#!
#! Inputs:
#! Operand stack: [NOTE_STORAGE_COMMITMENT, num_storage_items, dest_ptr]
#! Advice map: {
#! NOTE_STORAGE_COMMITMENT: [[INPUT_VALUES]]
#! }
#! Outputs:
#! Operand stack: []
proc write_storage_to_memory
# load the inputs from the advice map to the advice stack
# we pad the number of inputs to the next multiple of 8 so that we can use the
# `pipe_double_words_to_memory` instruction. The padded zeros don't affect the commitment
# computation.
adv.push_mapvaln.8
# OS => [NOTE_STORAGE_COMMITMENT, num_storage_items, dest_ptr]
# AS => [advice_num_storage_items, [INPUT_VALUES]]
# move the number of inputs obtained from advice map to the operand stack
adv_push dup.5
# OS => [num_storage_items, advice_num_storage_items, NOTE_STORAGE_COMMITMENT, num_storage_items, dest_ptr]
# AS => [[INPUT_VALUES]]
assert_eq.err=ERR_NOTE_INVALID_NUMBER_OF_STORAGE_ITEMS
# OS => [NOTE_STORAGE_COMMITMENT, num_storage_items, dest_ptr]
# AS => [[INPUT_VALUES]]
# calculate the number of words required to store the inputs
dup.4 u32divmod.4 neq.0 add
# OS => [num_words, NOTE_STORAGE_COMMITMENT, num_storage_items, dest_ptr]
# AS => [[INPUT_VALUES]]
# round up the number of words to the next multiple of 2
dup is_odd add
# OS => [even_num_words, NOTE_STORAGE_COMMITMENT, num_storage_items, dest_ptr]
# AS => [[INPUT_VALUES]]
# compute the end pointer for writing the padded inputs (even_num_words * 4 elements)
dup.6 swap mul.4 add
# OS => [end_ptr, NOTE_STORAGE_COMMITMENT, num_storage_items, dest_ptr]
# AS => [[INPUT_VALUES]]
# prepare the stack for the `pipe_double_words_to_memory` procedure.
#
# To match `poseidon2::hash_elements` (used for NOTE_STORAGE_COMMITMENT), we set the first capacity
# element to `num_storage_items % 8`.
movup.6 movup.6
# OS => [num_storage_items, write_ptr, end_ptr, NOTE_STORAGE_COMMITMENT]
# AS => [[INPUT_VALUES]]
u32divmod.8 swap drop
# OS => [num_storage_items_mod_8, write_ptr, end_ptr, NOTE_STORAGE_COMMITMENT]
# AS => [[INPUT_VALUES]]
push.0.0.0 movup.3
# OS => [CAPACITY = [num_storage_items_mod_8, 0, 0, 0], write_ptr, end_ptr, NOTE_STORAGE_COMMITMENT]
# AS => [[INPUT_VALUES]]
padw padw
# OS => [RATE0, RATE1, CAPACITY, write_ptr, end_ptr, NOTE_STORAGE_COMMITMENT]
# AS => [[INPUT_VALUES]]
# write the inputs from the advice stack into memory
exec.mem::pipe_double_words_to_memory
# OS => [RATE0, RATE1, CAPACITY, end_ptr', NOTE_STORAGE_COMMITMENT]
# AS => []
# extract the computed commitment from the hasher state
exec.poseidon2::squeeze_digest
# OS => [COMPUTED_COMMITMENT, end_ptr', NOTE_STORAGE_COMMITMENT]
# drop end_ptr'
movup.4 drop
# OS => [COMPUTED_COMMITMENT, NOTE_STORAGE_COMMITMENT]
# validate that the inputs written to memory match the inputs commitment
assert_eqw.err=ERR_NOTE_DATA_DOES_NOT_MATCH_COMMITMENT
# => []
end
# ATTACHMENTS
# =================================================================================================
#! Returns the commitment over all attachments of the active note.
#!
#! Inputs: []
#! Outputs: [ATTACHMENTS_COMMITMENT]
#!
#! Where:
#! - ATTACHMENTS_COMMITMENT is the commitment to all attachments of the note, or the EMPTY_WORD if
#! the note does not have any attachments.
#!
#! Panics if:
#! - no note is currently active.
#!
#! Invocation: exec
pub proc get_attachments_commitment
# push a placeholder note_index (ignored when is_active_note = 1) and the active note flag
push.0.1
# => [is_active_note = 1, note_index = 0]
exec.input_note::get_attachments_commitment_raw
# => [ATTACHMENTS_COMMITMENT]
end
#! Writes the attachment commitments of the active note to the provided destination pointer.
#!
#! Inputs: [dest_ptr]
#! Outputs: [num_attachments]
#!
#! Where:
#! - dest_ptr is the memory address to which to write the attachment commitments.
#! - num_attachments is the number of attachments in the note.
#!
#! Panics if:
#! - no note is currently active.
#! - the sequential hash over the attachment commitments in the advice inputs does not match the
#! attachments commitment.
#!
#! Invocation: exec
pub proc write_attachment_commitments_to_memory
exec.get_attachments_commitment
# => [ATTACHMENTS_COMMITMENT, dest_ptr]
exec.note::write_attachment_commitments_to_memory
# => [num_attachments]
end
#! Writes the attachment with the provided index from the active note the provided destination
#! pointer.
#!
#! Inputs: [dest_ptr, attachment_idx]
#! Outputs: [num_words]
#!
#! Where:
#! - dest_ptr is the memory address to which to write the attachment data.
#! - attachment_idx is the index of the attachment to retrieve.
#! - num_words is the number of words in the attachment.
#!
#! Panics if:
#! - no note is currently active.
#! - the attachment index is greater or equal to the number of attachments.
#! - the sequential hash over the attachment data in the advice inputs does not match the
#! attachment commitment.
#!
#! Invocation: exec
@locals(16)
pub proc write_attachment_to_memory
# write the attachment commitments to local memory
# we allocate 16 elements of memory (max num attachments * WORD_SIZE) to store all
# four attachment commitments
swap locaddr.0
# => [attachment_commitments_ptr, attachment_idx, dest_ptr]
exec.write_attachment_commitments_to_memory
# => [num_attachments, attachment_idx, dest_ptr]
locaddr.0 swap
# => [num_attachments, attachment_commitments_ptr, attachment_idx, dest_ptr]
exec.note::write_indexed_attachment_to_memory
# => [num_words]
end
#! Searches the metadata of the active note for the specified attachment scheme and returns
#! the index of the first matching slot.
#!
#! Inputs: [attachment_scheme]
#! Outputs: [is_found, attachment_idx]
#!
#! Where:
#! - attachment_scheme is the scheme of the attachment to find.
#! - is_found is 1 if the attachment with the provided scheme was found, 0 otherwise.
#! - attachment_idx is the index (0-3) of the first matching slot, or undefined if not found.
#!
#! Panics if:
#! - no note is currently active.
#!
#! Invocation: exec
pub proc find_attachment
exec.get_metadata
# => [METADATA, attachment_scheme]
movup.4
# => [attachment_scheme, METADATA]
exec.note::find_attachment_idx
# => [is_found, attachment_idx]
end