use.$kernel::account
use.$kernel::account_delta
use.$kernel::account_id
use.$kernel::faucet
use.$kernel::input_note
use.$kernel::memory
use.$kernel::note
use.$kernel::output_note
use.$kernel::tx
# NOTE
# =================================================================================================
# `exec_kernel_proc` procedure is expected to be invoked using a `syscall` instruction. It makes #
# no guarantees about the contents of the `pad` elements shown in the inputs and outputs. It is #
# the caller's responsibility to make sure these elements do not contain any meaningful data. #
# All other procedures must be invoked using a `dynexec` instruction by their hashes stored in #
# the memory. #
# =================================================================================================
# ERRORS
# =================================================================================================
const.ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED="for faucets the FAUCET_STORAGE_DATA_SLOT storage slot is reserved and can not be used with set_account_item"
const.ERR_FAUCET_TOTAL_ISSUANCE_PROC_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_FAUCET="the faucet_get_total_fungible_asset_issuance procedure can only be called on a fungible faucet"
const.ERR_FAUCET_IS_NF_ASSET_ISSUED_PROC_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET="the faucet_is_non_fungible_asset_issued procedure can only be called on a non-fungible faucet"
const.ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS="provided kernel procedure offset is out of bounds"
const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_ASSETS_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note assets of active note because no note is currently being processed"
const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_RECIPIENT_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note recipient of active note because no note is currently being processed"
const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note metadata of active note because no note is currently being processed"
const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_INPUTS_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note inputs of active note because no note is currently being processed"
const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SCRIPT_ROOT_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note script root of active note because no note is currently being processed"
const.ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note serial number of active note because no note is currently being processed"
# AUTHENTICATION
# =================================================================================================
#! Authenticates that the invocation of a kernel procedure originates from the account context.
#!
#! Inputs: []
#! Outputs: [storage_offset, storage_size]
#!
#! Panics if:
#! - the invocation of the kernel procedure does not originate from the account context.
#!
#! Invocation: exec
proc.authenticate_account_origin
# get the hash of the caller
padw caller
# => [CALLER]
# assert that the caller is from the user context
exec.account::authenticate_and_track_procedure
# => [storage_offset, storage_size]
end
#! Asserts that the invocation of a kernel procedure originates from the authentication procedure of
#! the account.
#!
#! Inputs: []
#! Outputs: []
#!
#! Panics if:
#! - the invocation of the kernel procedure does not originate from the authentication procedure
#! of the account.
#!
#! Invocation: exec
proc.assert_auth_procedure_origin
# get the hash of the caller
padw caller
# => [CALLER]
# assert that the caller is from the user context
exec.account::assert_auth_procedure
# => []
end
# KERNEL PROCEDURES
# =================================================================================================
# ACCOUNT
# -------------------------------------------------------------------------------------------------
#! Returns the active account commitment at the beginning of the transaction.
#!
#! Inputs: [pad(16)]
#! Outputs: [INIT_COMMITMENT, pad(12)]
#!
#! Where:
#! - INIT_COMMITMENT is the initial account commitment.
#!
#! Invocation: dynexec
export.account_get_initial_commitment
# get the initial account commitment
exec.account::get_initial_commitment
# => [INIT_COMMITMENT, pad(16)]
# truncate the stack
swapw dropw
# => [INIT_COMMITMENT, pad(12)]
end
#! Computes commitment to the state of the active account.
#!
#! Inputs: [pad(16)]
#! Outputs: [ACCOUNT_COMMITMENT, pad(12)]
#!
#! Where:
#! - ACCOUNT_COMMITMENT is the commitment of the account data.
#!
#! Invocation: dynexec
export.account_compute_commitment
# compute the active account commitment
exec.account::compute_commitment
# => [ACCOUNT_COMMITMENT, pad(16)]
# truncate the stack
swapw dropw
# => [ACCOUNT_COMMITMENT, pad(12)]
end
#! Computes the commitment to the native account's delta.
#!
#! The commitment to an empty delta is defined as the empty word.
#!
#! During an account-creating transaction (when the initial nonce is 0), this procedure may not
#! return the empty word even if the initial storage commitment and the current storage commitment
#! are identical (storage hasn't changed). This is because the delta for a new account must
#! represent its entire newly created state, and the initial storage in a transaction is
#! initialized to the the storage that the account ID commits to, which may be non-empty. This
#! does not have any consequences other than being inconsistent in this edge case.
#!
#! Inputs: [pad(16)]
#! Outputs: [DELTA_COMMITMENT, pad(12)]
#!
#! Where:
#! - DELTA_COMMITMENT is the commitment to the account delta.
#!
#! Panics if:
#! - the vault or storage delta is not empty but the nonce increment is zero.
#!
#! Invocation: dynexec
export.account_compute_delta_commitment
# compute the account delta commitment
exec.account_delta::compute_commitment
# => [DELTA_COMMITMENT, pad(16)]
# truncate the stack
swapw dropw
# => [ACCOUNT_COMMITMENT, pad(12)]
end
#! Returns the ID of the specified account.
#!
#! Inputs: [is_native, pad(15)]
#! Outputs: [account_id_prefix, account_id_suffix, pad(14)]
#!
#! Where:
#! - is_native is a boolean flag that indicates whether the account ID was requested for the native
#! or the active account.
#! - account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the active account.
#!
#! Invocation: dynexec
export.account_get_id
# get the native account ID
exec.memory::get_native_account_id
# => [native_account_id_prefix, native_account_id_suffix, is_native, pad(15)]
# get the active account ID
exec.account::get_id
# => [
# active_account_id_prefix, active_account_id_suffix,
# native_account_id_prefix, native_account_id_suffix,
# is_native, pad(15)
# ]
# prepare the stack for the first cdrop
movup.2 dup.4
# => [
# is_native, native_account_id_prefix, active_account_id_prefix,
# active_account_id_suffix, native_account_id_suffix, is_native, pad(15)
# ]
# drop the prefix corresponding to the is_native flag
cdrop
# => [account_id_prefix, active_account_id_suffix, native_account_id_suffix, is_native, pad(15)]
# prepare the stack for the second cdrop
movdn.3 swap movup.2
# => [is_native, native_account_id_suffix, active_account_id_suffix, account_id_prefix, pad(15)]
# drop the suffix corresponding to the is_native flag
cdrop
# => [account_id_suffix, account_id_prefix, pad(15)]
# rearrange the ID parts and truncate the stack
swap movup.2 drop
# => [account_id_prefix, account_id_suffix, pad(14)]
end
#! Returns the active account nonce.
#!
#! Inputs: [pad(16)]
#! Outputs: [nonce, pad(15)]
#!
#! Where:
#! - nonce is the active account's nonce.
#!
#! Invocation: dynexec
export.account_get_nonce
# get the account nonce
exec.account::get_nonce
# => [nonce, pad(16)]
# truncate the stack
swap drop
# => [nonce, pad(15)]
end
#! Increments the account nonce by one and returns the new nonce.
#!
#! Inputs: [pad(16)]
#! Outputs: [final_nonce, pad(15)]
#!
#! Where:
#! - final_nonce is the new nonce of the account. Since it cannot be incremented again, this will
#! also be the final nonce of the account after transaction execution.
#!
#! Panics if:
#! - the invocation of this procedure does not originate from the native account.
#! - the invocation of this procedure does not originate from the authentication procedure
#! of the account.
#! - the nonce has already been incremented.
#!
#! Invocation: dynexec
export.account_incr_nonce
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [pad(16)]
# assert that nonce increment originates from the authentication procedure of the account
exec.assert_auth_procedure_origin
# => [pad(16)]
# increment the account nonce
exec.account::incr_nonce
# => [final_nonce, pad(16)]
# truncate the stack
swap drop
# => [final_nonce, pad(15)]
end
#! Gets the account code commitment of the active account.
#!
#! Inputs: [pad(16)]
#! Outputs: [CODE_COMMITMENT, pad(12)]
#!
#! Where:
#! - CODE_COMMITMENT is the commitment of the account code.
#!
#! Invocation: dynexec
export.account_get_code_commitment
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin drop drop
# => [pad(16)]
# get the account code commitment
exec.account::get_code_commitment
# => [CODE_COMMITMENT, pad(16)]
# truncate the stack
swapw dropw
# => [CODE_COMMITMENT, pad(12)]
end
#! Returns the storage commitment of the active account at the beginning of the transaction.
#!
#! Inputs: [pad(16)]
#! Outputs: [INIT_STORAGE_COMMITMENT, pad(12)]
#!
#! Where:
#! - INIT_STORAGE_COMMITMENT is the initial account storage commitment.
#!
#! Invocation: dynexec
export.account_get_initial_storage_commitment
# get the initial account storage commitment
exec.account::get_initial_storage_commitment
# => [INIT_STORAGE_COMMITMENT, pad(16)]
# truncate the stack
swapw dropw
# => [INIT_STORAGE_COMMITMENT, pad(12)]
end
#! Computes the latest account storage commitment of the active account.
#!
#! Inputs: [pad(16)]
#! Outputs: [STORAGE_COMMITMENT, pad(12)]
#!
#! Where:
#! - STORAGE_COMMITMENT is the commitment of the account storage.
#!
#! Invocation: dynexec
export.account_compute_storage_commitment
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin drop drop
# => [pad(16)]
# compute the account storage commitment
exec.account::compute_storage_commitment
# => [STORAGE_COMMITMENT, pad(16)]
# truncate the stack
swapw dropw
# => [STORAGE_COMMITMENT, pad(12)]
end
#! Gets an item from the account storage.
#!
#! Inputs: [index, pad(15)]
#! Outputs: [VALUE, pad(12)]
#!
#! Where:
#! - index is the index of the item to get.
#! - VALUE is the value of the item.
#!
#! Panics if:
#! - the index is out of bounds.
#!
#! Invocation: dynexec
export.account_get_item
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [storage_offset, storage_size, index, pad(15)]
# apply offset to storage slot index
exec.account::apply_storage_offset
# => [index_with_offset, pad(15)]
# fetch the account storage item
exec.account::get_item
# => [VALUE, pad(15)]
# truncate the stack
movup.4 drop movup.4 drop movup.4 drop
# => [VALUE, pad(12)]
end
#! Sets an item in the account storage.
#!
#! Inputs: [index, VALUE, pad(11)]
#! Outputs: [OLD_VALUE, pad(12)]
#!
#! Where:
#! - index is the index of the item to set.
#! - VALUE is the value to set.
#! - OLD_VALUE is the previous value of the item.
#!
#! Panics if:
#! - the index is out of bounds.
#! - the invocation of this procedure does not originate from the native account.
#!
#! Invocation: dynexec
export.account_set_item
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [index, VALUE, pad(11)]
# if the transaction is being executed against a faucet account then assert
# index != FAUCET_STORAGE_DATA_SLOT (reserved slot)
dup exec.account::get_faucet_storage_data_slot eq
exec.account::get_id swap drop exec.account_id::is_faucet
and assertz.err=ERR_FAUCET_STORAGE_DATA_SLOT_IS_RESERVED
# => [index, VALUE, pad(11)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [storage_offset, storage_size, index, VALUE, pad(11)]
# apply offset to storage slot index
exec.account::apply_storage_offset
# => [index_with_offset, VALUE, pad(11)]
# set the account storage item
exec.account::set_item
# => [OLD_VALUE, pad(12)]
end
#! Returns the VALUE located under the specified KEY within the map contained in the given
#! account storage slot.
#!
#! Inputs: [index, KEY, pad(11)]
#! Outputs: [VALUE, pad(12)]
#!
#! Where:
#! - index is the index of the storage slot that contains the map root.
#! - VALUE is the value of the map item at KEY.
#!
#! Panics if:
#! - the index is out of bounds (>255).
#! - the requested storage slot type is not map.
#!
#! Invocation: dynexec
export.account_get_map_item
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [storage_offset, storage_size, index, KEY, pad(11)]
# apply offset to storage slot index
exec.account::apply_storage_offset
# => [index_with_offset, KEY, pad(11)]
# fetch the map item from account storage
exec.account::get_map_item
# => [VALUE, pad(12)]
end
#! Gets an item from the account storage at its initial state (beginning of transaction).
#!
#! Inputs: [index, pad(15)]
#! Outputs: [INIT_VALUE, pad(12)]
#!
#! Where:
#! - index is the index of the item to get.
#! - INIT_VALUE is the initial value of the item at the beginning of the transaction.
#!
#! Panics if:
#! - the index is out of bounds.
#!
#! Invocation: dynexec
export.account_get_initial_item
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [storage_offset, storage_size, index, pad(15)]
# apply offset to storage slot index
exec.account::apply_storage_offset
# => [index_with_offset, pad(15)]
# fetch the initial account storage item
exec.account::get_initial_item
# => [INIT_VALUE, pad(15)]
# truncate the stack
movup.4 drop movup.4 drop movup.4 drop
# => [INIT_VALUE, pad(12)]
end
#! Returns the initial VALUE located under the specified KEY within the map contained in the given
#! account storage slot at the beginning of the transaction.
#!
#! Inputs: [index, KEY, pad(11)]
#! Outputs: [INIT_VALUE, pad(12)]
#!
#! Where:
#! - index is the index of the storage slot that contains the map root.
#! - INIT_VALUE is the initial value of the map item at KEY at the beginning of the transaction.
#!
#! Panics if:
#! - the index is out of bounds (>255).
#! - the requested storage slot type is not map.
#!
#! Invocation: dynexec
export.account_get_initial_map_item
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [storage_offset, storage_size, index, KEY, pad(11)]
# apply offset to storage slot index
exec.account::apply_storage_offset
# => [index_with_offset, KEY, pad(11)]
# fetch the initial map item from account storage
exec.account::get_initial_map_item
# => [INIT_VALUE, pad(12)]
end
#! Stores NEW_VALUE under the specified KEY within the map contained in the given account storage
#! slot.
#!
#! Inputs: [index, KEY, NEW_VALUE, pad(7)]
#! Outputs: [OLD_MAP_ROOT, OLD_MAP_VALUE, pad(8)]
#!
#! Where:
#! - index is the index of the storage slot which contains the map root.
#! - NEW_VALUE is the value of the new map item for the respective KEY.
#! - OLD_VALUE is the value of the old map item for the respective KEY.
#! - KEY is the key of the new item.
#! - OLD_MAP_ROOT is the root of the old map before insertion
#!
#! Panics if:
#! - the index is out of bounds (>255).
#! - the requested storage slot type is not map.
#! - the procedure is called from a non-account context.
#! - the invocation of this procedure does not originate from the native account.
#!
#! Invocation: dynexec
export.account_set_map_item
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [index, KEY, NEW_VALUE, pad(7)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [storage_offset, storage_size, index, KEY, NEW_VALUE, pad(7)]
# apply offset to storage slot index
exec.account::apply_storage_offset
# => [index_with_offset, KEY, NEW_VALUE, pad(7)]
# set the new map item
exec.account::set_map_item
# => [OLD_MAP_ROOT, OLD_VALUE, pad(8)]
end
#! Returns the vault root of the active account at the beginning of the transaction.
#!
#! Inputs: [pad(16)]
#! Outputs: [INIT_VAULT_ROOT, pad(12)]
#!
#! Where:
#! - INIT_VAULT_ROOT is the initial account vault root.
#!
#! Invocation: dynexec
export.account_get_initial_vault_root
# get the initial account vault root
exec.account::get_initial_vault_root
# => [INIT_VAULT_ROOT, pad(16)]
# truncate the stack
swapw dropw
# => [INIT_VAULT_ROOT, pad(12)]
end
#! Returns the vault root of the active account.
#!
#! Inputs: [pad(16)]
#! Outputs: [VAULT_ROOT, pad(12)]
#!
#! Where:
#! - VAULT_ROOT is the root of the account vault.
#!
#! Invocation: dynexec
export.account_get_vault_root
# fetch the account vault root
exec.memory::get_account_vault_root
# => [VAULT_ROOT, pad(16)]
# truncate the stack
swapw dropw
# => [VAULT_ROOT, pad(12)]
end
#! Adds the specified asset to the vault.
#!
#! Inputs: [ASSET, pad(12)]
#! Outputs: [ASSET', pad(12)]
#!
#! Where:
#! - ASSET is the asset to add to the vault.
#! - ASSET' final asset in the account vault defined as follows:
#! - If ASSET is a non-fungible asset, then ASSET' is the same as ASSET.
#! - If ASSET is a fungible asset, then ASSET' is the total fungible asset in the account vault
#! after ASSET was added to it.
#!
#! Panics if:
#! - the asset is not valid.
#! - the total value of the fungible asset is greater than or equal to 2^63 after the new asset was
#! added.
#! - the vault already contains the same non-fungible asset.
#! - the invocation of this procedure does not originate from the native account.
#!
#! Invocation: dynexec
export.account_add_asset
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [ASSET, pad(12)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin drop drop
# => [ASSET, pad(12)]
# add the specified asset to the account vault, emitting the corresponding events
exec.account::add_asset_to_vault
# => [ASSET', pad(12)]
end
#! Removes the specified asset from the vault.
#!
#! Inputs: [ASSET, pad(12)]
#! Outputs: [ASSET, pad(12)]
#!
#! Where:
#! - ASSET is the asset to remove from the vault.
#!
#! Panics if:
#! - the fungible asset is not found in the vault.
#! - the amount of the fungible asset in the vault is less than the amount to be removed.
#! - the non-fungible asset is not found in the vault.
#! - the invocation of this procedure does not originate from the native account.
#!
#! Invocation: dynexec
export.account_remove_asset
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [ASSET, pad(12)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin drop drop
# => [ASSET, pad(12)]
# remove the specified asset from the account vault, emitting the corresponding events
exec.account::remove_asset_from_vault
# => [ASSET, pad(12)]
end
#! Returns the balance of the fungible asset associated with the provided faucet_id in the active
#! account's vault.
#!
#! Inputs: [faucet_id_prefix, faucet_id_suffix, pad(14)]
#! Outputs: [balance, pad(15)]
#!
#! Where:
#! - faucet_id_{prefix,suffix} are the prefix and suffix felts of the faucet id of the fungible
#! asset of interest.
#! - balance is the vault balance of the fungible asset.
#!
#! Panics if:
#! - the provided faucet ID is not an ID of a fungible faucet.
#!
#! Invocation: dynexec
export.account_get_balance
exec.account::get_balance
# => [balance, pad(15)]
end
#! Returns the balance of the fungible asset associated with the provided faucet_id in the active
#! account's vault at the beginning of the transaction.
#!
#! Inputs: [faucet_id_prefix, faucet_id_suffix, pad(14)]
#! Outputs: [init_balance, pad(15)]
#!
#! Where:
#! - faucet_id_{prefix,suffix} are the prefix and suffix felts of the faucet id of the fungible
#! asset of interest.
#! - init_balance is the vault balance of the fungible asset at the beginning of the transaction.
#!
#! Panics if:
#! - the provided faucet ID is not an ID of a fungible faucet.
#!
#! Invocation: dynexec
export.account_get_initial_balance
exec.account::get_initial_balance
# => [init_balance, pad(15)]
end
#! Returns a boolean indicating whether the non-fungible asset is present in the active account's
#! vault.
#!
#! Inputs: [ASSET, pad(12)]
#! Outputs: [has_asset, pad(15)]
#!
#! Where:
#! - ASSET is the non-fungible asset of interest.
#! - has_asset is a boolean indicating whether the account vault has the asset of interest.
#!
#! Panics if:
#! - the ASSET is a fungible asset.
#!
#! Invocation: dynexec
export.account_has_non_fungible_asset
exec.account::has_non_fungible_asset
# => [has_asset, pad(15)]
end
#! Returns 1 if a native account procedure was called during transaction execution, and 0 otherwise.
#!
#! Inputs: [PROC_ROOT, pad(12)]
#! Outputs: [was_called, pad(15)]
#!
#! Where:
#! - PROC_ROOT is the hash of the procedure to check.
#! - was_called is 1 if the procedure was called at least once during tx execution, 0 otherwise.
#!
#! Panics if:
#! - the procedure root is not part of the account code.
#!
#! Invocation: dynexec
export.account_was_procedure_called
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [PROC_ROOT, pad(12)]
# check if the procedure was called
exec.account::was_procedure_called
# => [was_called, pad(15)]
end
#! Returns the number of procedures in the active account.
#!
#! Inputs: [pad(16)]
#! Outputs: [num_procedures, pad(15)]
#!
#! Where:
#! - num_procedures is the number of procedures in the active account.
#!
#! Invocation: dynexec
export.account_get_num_procedures
# get the number of procedures
exec.memory::get_num_account_procedures
# => [num_procedures, pad(16)]
# truncate the stack
swap drop
# => [num_procedures, pad(15)]
end
#! Returns the procedure root for the active account procedure at the specified index.
#!
#! Inputs: [index, pad(15)]
#! Outputs: [PROC_ROOT, pad(12)]
#!
#! Where:
#! - index is the index of the procedure.
#! - PROC_ROOT is the hash of the procedure.
#!
#! Panics if:
#! - the procedure index is out of bounds.
#!
#! Invocation: dynexec
export.account_get_procedure_root
# get the procedure information
exec.account::get_procedure_info
# => [PROC_ROOT, storage_offset, storage_size, pad(15)]
swapw dropw
# => [PROC_ROOT, pad(13)]
movup.4 drop
# => [PROC_ROOT, pad(12)]
end
#! Returns the binary flag indicating whether the procedure with the provided root is available on
#! the active account.
#!
#! Returns 1 if the procedure is available on the active account and 0 otherwise.
#!
#! Inputs: [PROC_ROOT, pad(12)]
#! Outputs: [is_procedure_available, pad(15)]
#!
#! Where:
#! - PROC_ROOT is the hash of the procedure of interest.
#! - is_procedure_available is the binary flag indicating whether the procedure with PROC_ROOT is
#! available on the active account.
#!
#! Invocation: dynexec
export.account_has_procedure
# check if the procedure was called
exec.account::has_procedure
# => [is_procedure_available, pad(15)]
end
# FAUCET
# -------------------------------------------------------------------------------------------------
#! Mint an asset from the faucet the transaction is being executed against.
#!
#! Inputs: [ASSET, pad(12)]
#! Outputs: [ASSET, pad(12)]
#!
#! Where:
#! - ASSET is the asset that was minted.
#!
#! Panics if:
#! - the transaction is not being executed against a faucet.
#! - the invocation of this procedure does not originate from the native account.
#! - the asset being minted is not associated with the faucet the transaction is being executed
#! against.
#! - the asset is not well formed.
#! - For fungible faucets:
#! - if the total issuance after minting is greater than the maximum amount allowed.
#! - For non-fungible faucets:
#! - if the non-fungible asset being minted already exists.
#!
#! Invocation: dynexec
export.faucet_mint_asset
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [ASSET, pad(12)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin drop drop
# => [ASSET, pad(12)]
# mint the asset
exec.faucet::mint
# => [ASSET, pad(12)]
end
#! Burn an asset from the faucet the transaction is being executed against.
#!
#! Inputs: [ASSET, pad(12)]
#! Outputs: [ASSET, pad(12)]
#!
#! Where:
#! - ASSET is the asset that was burned.
#!
#! Panics if:
#! - the transaction is not being executed against a faucet.
#! - the invocation of this procedure does not originate from the native account.
#! - the asset being burned is not associated with the faucet the transaction is being executed
#! against.
#! - the asset is not well formed.
#! - For fungible faucets:
#! - the amount being burned is greater than the total input to the transaction.
#! - For non-fungible faucets:
#! - the non-fungible asset being burned does not exist or was not provided as input to the
#! transaction via a note or the accounts vault.
#!
#! Invocation: dynexec
export.faucet_burn_asset
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [ASSET, pad(12)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin drop drop
# => [ASSET, pad(12)]
# burn the asset
exec.faucet::burn
# => [ASSET, pad(12)]
end
#! Returns the total issuance of the fungible faucet the transaction is being executed against.
#!
#! Inputs: [pad(16)]
#! Outputs: [total_issuance, pad(15)]
#!
#! Where:
#! - total_issuance is the total issuance of the fungible faucet the transaction is being executed
#! against.
#!
#! Panics if:
#! - the transaction is not being executed against a fungible faucet.
#!
#! Invocation: dynexec
export.faucet_get_total_fungible_asset_issuance
# assert that we are executing a transaction against a fungible faucet (access checks)
exec.account::get_id swap drop exec.account_id::is_fungible_faucet
assert.err=ERR_FAUCET_TOTAL_ISSUANCE_PROC_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_FAUCET
# => [pad(16)]
# get the total issuance
exec.faucet::get_total_issuance
# => [total_issuance, pad(16)]
# truncate the stack
swap drop
# => [total_issuance, pad(15)]
end
#! Returns a boolean indicating whether the provided non-fungible asset has been already issued by
#! this faucet.
#!
#! Inputs: [ASSET, pad(12)]
#! Outputs: [is_issued, pad(15)]
#!
#! Where:
#! - ASSET is the non-fungible asset that is being checked.
#! - is_issued is a boolean indicating whether the non-fungible asset has been issued.
#!
#! Panics if:
#! - the ASSET is a fungible asset.
#! - the ASSET is not associated with the faucet the transaction is being executed against.
#!
#! Invocation: dynexec
export.faucet_is_non_fungible_asset_issued
# assert that we are executing a transaction against a non-fungible faucet (access checks)
exec.account::get_id swap drop exec.account_id::is_non_fungible_faucet
assert.err=ERR_FAUCET_IS_NF_ASSET_ISSUED_PROC_CAN_ONLY_BE_CALLED_ON_NON_FUNGIBLE_FAUCET
# => [ASSET, pad(12)]
# get the issuance flag
exec.faucet::is_non_fungible_asset_issued
# => [is_issued, pad(16)]
# truncate the stack
swap drop
# => [is_issued, pad(15)]
end
# INPUT NOTE
# -------------------------------------------------------------------------------------------------
#! Returns the assets information of the specified input note.
#!
#! Inputs: [is_active_note, note_index, pad(14)]
#! Outputs: [ASSETS_COMMITMENT, num_assets, pad(11)]
#!
#! Where:
#! - is_active_note is the boolean flag indicating whether we should return the assets data from
#! the active note or from the note with the specified index.
#! - note_index is the index of the input note whose assets info should be returned. Notice that if
#! is_active_note is 1, note_index is ignored.
#! - ASSETS_COMMITMENT is a sequential hash of the assets in the specified input note.
#! - num_assets is the number of assets in the specified input note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of input notes.
#! - is_active_note is 1 and no input note is not being processed (attempted to access note inputs
#! from incorrect context).
#!
#! Invocation: dynexec
export.input_note_get_assets_info
# get the input note pointer depending on whether the requested note is current or it was
# requested by index.
exec.get_requested_note_ptr
# => [input_note_ptr, pad(15)]
# assert the pointer is not zero - this would suggest the procedure has been called from an
# incorrect context
dup neq.0 assert.err=ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_ASSETS_WHILE_NO_NOTE_BEING_PROCESSED
# => [input_note_ptr, pad(15)]
# get the assets info
exec.input_note::get_assets_info
# => [ASSETS_COMMITMENT, num_assets, pad(15)]
# truncate the stack
movupw.2 dropw
# => [ASSETS_COMMITMENT, num_assets, pad(11)]
end
#! Returns the recipient of the specified input note.
#!
#! Inputs: [is_active_note, note_index, pad(15)]
#! Outputs: [RECIPIENT, pad(12)]
#!
#! Where:
#! - is_active_note is the boolean flag indicating whether we should return the assets data from
#! the active note or from the note with the specified index.
#! - note_index is the index of the input note whose assets info should be returned. Notice that if
#! is_active_note is 1, note_index is ignored.
#! - RECIPIENT is the commitment to the input note's script, inputs, the serial number.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of input notes.
#! - is_active_note is 1 and no input note is not being processed (attempted to access note inputs
#! from incorrect context).
#!
#! Invocation: dynexec
export.input_note_get_recipient
# get the input note pointer depending on whether the requested note is current or it was
# requested by index.
exec.get_requested_note_ptr
# => [input_note_ptr, pad(15)]
# assert the pointer is not zero - this would suggest the procedure has been called from an
# incorrect context
dup neq.0 assert.err=ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_RECIPIENT_WHILE_NO_NOTE_BEING_PROCESSED
# => [input_note_ptr, pad(15)]
# get the recipient
exec.memory::get_input_note_recipient
# => [RECIPIENT, pad(15)]
# truncate the stack
swapw drop drop drop movdn.4
# => [RECIPIENT, pad(12)]
end
#! Returns the metadata of the specified input note.
#!
#! Inputs: [is_active_note, note_index, pad(14)]
#! Outputs: [METADATA, pad(12)]
#!
#! Where:
#! - is_active_note is the boolean flag indicating whether we should return the metadata from
#! the active note or from the note with the specified index.
#! - note_index is the index of the input note whose metadata should be returned. Notice that if
#! is_active_note is 1, note_index is ignored.
#! - METADATA is the metadata of the specified input note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of input notes.
#! - is_active_note is 1 and no input note is not being processed (attempted to access note inputs
#! from incorrect context).
#!
#! Invocation: dynexec
export.input_note_get_metadata
# get the input note pointer depending on whether the requested note is current or it was
# requested by index.
exec.get_requested_note_ptr
# => [input_note_ptr, pad(15)]
# assert the pointer is not zero - this would suggest the procedure has been called from an
# incorrect context
dup neq.0 assert.err=ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_METADATA_WHILE_NO_NOTE_BEING_PROCESSED
# => [input_note_ptr, pad(15)]
# get the metadata
exec.memory::get_input_note_metadata
# => [METADATA, pad(15)]
# truncate the stack
swapw drop drop drop movdn.4
# => [METADATA, pad(12)]
end
#! Returns the serial number of the specified input note.
#!
#! Inputs: [is_active_note, note_index, pad(14)]
#! Outputs: [SERIAL_NUMBER, pad(12)]
#!
#! Where:
#! - is_active_note is the boolean flag indicating whether we should return the serial number
#! of the active note or of the note with the specified index.
#! - note_index is the index of the input note whose serial number should be returned. Notice that
#! if is_active_note is 1, note_index is ignored.
#! - SERIAL_NUMBER is the serial number of the specified input note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of input notes.
#! - is_active_note is 1 and no input note is not being processed (attempted to access note inputs
#! from incorrect context).
#!
#! Invocation: dynexec
export.input_note_get_serial_number
# get the input note pointer depending on whether the requested note is current or it was
# requested by index.
exec.get_requested_note_ptr
# => [input_note_ptr, pad(15)]
# assert the pointer is not zero - this would suggest the procedure has been called from an
# incorrect context
dup neq.0 assert.err=ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSED
# => [input_note_ptr, pad(15)]
# get the serial number using the note pointer
exec.memory::get_input_note_serial_num
# => [SERIAL_NUMBER, pad(15)]
# truncate the stack
repeat.3
movup.4 drop
end
# => [SERIAL_NUMBER, pad(12)]
end
#! Returns the inputs commitment and length of the specified input note.
#!
#! Inputs: [is_active_note, note_index, pad(14)]
#! Outputs: [NOTE_INPUTS_COMMITMENT, num_inputs, pad(11)]
#!
#! Where:
#! - is_active_note is the boolean flag indicating whether we should return the inputs commitment
#! and length from the active note or from the note with the specified index.
#! - note_index is the index of the input note whose data should be returned. Notice that if
#! is_active_note is 1, note_index is ignored.
#! - NOTE_INPUTS_COMMITMENT is the inputs commitment of the specified input note.
#! - num_inputs is the number of inputs of the specified input note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of input notes.
#! - is_active_note is 1 and no input note is not being processed (attempted to access note inputs
#! from incorrect context).
#!
#! Invocation: dynexec
export.input_note_get_inputs_info
# get the input note pointer depending on whether the requested note is current or it was
# requested by index.
exec.get_requested_note_ptr
# => [input_note_ptr, pad(15)]
# assert the pointer is not zero - this would suggest the procedure has been called from an
# incorrect context
dup neq.0 assert.err=ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_INPUTS_WHILE_NO_NOTE_BEING_PROCESSED
# => [input_note_ptr, pad(15)]
# get the note inputs length
dup exec.memory::get_input_note_num_inputs swap
# => [input_note_ptr, num_inputs, pad(16)]
# get the inputs commitment
exec.memory::get_input_note_inputs_commitment
# => [NOTE_INPUTS_COMMITMENT, num_inputs, pad(16)]
# truncate the stack
repeat.5
movup.5 drop
end
# => [NOTE_INPUTS_COMMITMENT, num_inputs, pad(11)]
end
#! Returns the script root of the specified input note.
#!
#! Inputs: [is_active_note, note_index, pad(14)]
#! Outputs: [SCRIPT_ROOT, pad(12)]
#!
#! Where:
#! - is_active_note is the boolean flag indicating whether we should return the inputs commitment
#! and length from the active note or from the note with the specified index.
#! - note_index is the index of the input note whose data should be returned. Notice that if
#! is_active_note is 1, note_index is ignored.
#! - SCRIPT_ROOT is the script root of the specified input note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of input notes.
#! - is_active_note is 1 and no input note is not being processed (attempted to access note inputs
#! from incorrect context).
#!
#! Invocation: dynexec
export.input_note_get_script_root
# get the input note pointer depending on whether the requested note is current or it was
# requested by index.
exec.get_requested_note_ptr
# => [input_note_ptr, pad(15)]
# assert the pointer is not zero - this would suggest the procedure has been called from an
# incorrect context
dup neq.0 assert.err=ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SCRIPT_ROOT_WHILE_NO_NOTE_BEING_PROCESSED
# => [input_note_ptr, pad(15)]
# get the script root using the note pointer
exec.memory::get_input_note_script_root
# => [SCRIPT_ROOT, pad(15)]
# truncate the stack
repeat.3
movup.4 drop
end
# => [SCRIPT_ROOT, pad(12)]
end
# OUTPUT NOTE
# -------------------------------------------------------------------------------------------------
#! Creates a new note and returns its index.
#!
#! Inputs: [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)]
#! Outputs: [note_idx, pad(15)]
#!
#! Where:
#! - tag is the tag to be included in the note.
#! - aux is the auxiliary metadata to be included in the note.
#! - note_type is the note storage type.
#! - execution_hint is the note execution hint tag and payload.
#! - RECIPIENT is the recipient of the note.
#! - note_idx is the index of the created note.
#!
#! Invocation: dynexec
export.output_note_create
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [tag, aux, note_type, execution_hint, RECIPIENT, pad(8)]
exec.output_note::create
# => [note_idx, pad(15)]
end
#! Adds the ASSET to the note specified by the index.
#!
#! Inputs: [note_idx, ASSET, pad(11)]
#! Outputs: [pad(16)]
#!
#! Where:
#! - note_idx is the index of the note to which the asset is added.
#! - ASSET can be a fungible or non-fungible asset.
#!
#! Panics if:
#! - the procedure is called when the active account is not the native one.
#!
#! Invocation: dynexec
export.output_note_add_asset
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [note_idx, ASSET, pad(11)]
exec.output_note::add_asset
# => [pad(16)]
end
#! Returns the information about assets in the output note with the specified index.
#!
#! Inputs: [note_index, pad(15)]
#! Outputs: [ASSETS_COMMITMENT, num_assets, pad(11)]
#!
#! Where:
#! - note_index is the index of the output note whose assets info should be returned.
#! - num_assets is the number of assets in the specified note.
#! - ASSETS_COMMITMENT is a sequential hash of the assets in the specified note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of output notes.
#!
#! Invocation: dynexec
export.output_note_get_assets_info
# assert that the provided note index is less than the total number of output notes
exec.output_note::assert_note_index_in_bounds
# => [note_index, pad(15)]
# get the assets info
exec.output_note::get_assets_info
# => [ASSETS_COMMITMENT, num_assets, pad(16)]
# truncate the stack
repeat.5
movup.5 drop
end
# => [ASSETS_COMMITMENT, num_assets, pad(11)]
end
#! Returns the recipient of the output note with the specified index.
#!
#! Inputs: [note_index, pad(15)]
#! Outputs: [RECIPIENT, pad(12)]
#!
#! Where:
#! - note_index is the index of the output note whose recipient should be returned.
#! - RECIPIENT is the commitment to the output note's script, inputs, the serial number.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of output notes.
#!
#! Invocation: dynexec
export.output_note_get_recipient
# assert that the provided note index is less than the total number of output notes
exec.output_note::assert_note_index_in_bounds
# => [note_index, pad(15)]
# get the note data pointer by the provided index
exec.memory::get_output_note_ptr
# => [note_ptr, pad(15)]
# get the recipient
exec.memory::get_output_note_recipient
# => [RECIPIENT, pad(15)]
# truncate the stack
swapw drop drop drop movdn.4
# => [RECIPIENT, pad(12)]
end
#! Returns the metadata of the output note with the specified index.
#!
#! Inputs: [note_index, pad(15)]
#! Outputs: [METADATA, pad(12)]
#!
#! Where:
#! - note_index is the index of the output note whose metadata should be returned.
#! - METADATA is the metadata of the output note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of output notes.
#!
#! Invocation: dynexec
export.output_note_get_metadata
# assert that the provided note index is less than the total number of output notes
exec.output_note::assert_note_index_in_bounds
# => [note_index, pad(15)]
# get the note data pointer by the provided index
exec.memory::get_output_note_ptr
# => [note_ptr, pad(15)]
# get the metadata
exec.memory::get_output_note_metadata
# => [METADATA, pad(15)]
# truncate the stack
swapw drop drop drop movdn.4
# => [METADATA, pad(12)]
end
# TRANSACTION
# -------------------------------------------------------------------------------------------------
#! Returns the input notes commitment.
#!
#! This is computed as a sequential hash of `(NULLIFIER, EMPTY_WORD_OR_NOTE_COMMITMENT)` over all
#! input notes. The data `EMPTY_WORD_OR_NOTE_COMMITMENT` functions as a flag, if the value is set to
#! zero, then the notes are authenticated by the transaction kernel. If the value is non-zero, then
#! note authentication will be delayed to the batch/block kernel. The delayed authentication allows
#! a transaction to consume a public note that is not yet included to a block.
#!
#! Inputs: [pad(16)]
#! Outputs: [INPUT_NOTES_COMMITMENT, pad(12)]
#!
#! Where:
#! - INPUT_NOTES_COMMITMENT is the input notes commitment hash.
#!
#! Invocation: dynexec
export.tx_get_input_notes_commitment
exec.tx::get_input_notes_commitment
# => [INPUT_NOTES_COMMITMENT, pad(16)]
# truncate the stack
swapw dropw
# => [INPUT_NOTES_COMMITMENT, pad(12)]
end
#! Returns the output notes commitment. This is computed as a sequential hash of
#! (note_id, note_metadata) tuples over all output notes.
#!
#! Inputs: [pad(16)]
#! Outputs: [OUTPUT_NOTES_COMMITMENT, pad(12)]
#!
#! Where:
#! - OUTPUT_NOTES_COMMITMENT is the output notes commitment.
#!
#! Invocation: dynexec
export.tx_get_output_notes_commitment
# get the output notes commitment
exec.tx::get_output_notes_commitment
# => [OUTPUT_NOTES_COMMITMENT, pad(16)]
# truncate the stack
swapw dropw
# => [OUTPUT_NOTES_COMMITMENT, pad(12)]
end
#! Returns the total number of input notes consumed by this transaction.
#!
#! Inputs: [pad(16)]
#! Outputs: [num_input_notes, pad(15)]
#!
#! Where:
#! - num_input_notes is the total number of input notes consumed by this transaction.
#!
#! Invocation: dynexec
export.tx_get_num_input_notes
# get the number of input notes
exec.tx::get_num_input_notes
# => [num_input_notes, pad(16)]
# truncate the stack
swap drop
# => [num_input_notes, pad(15)]
end
#! Returns the current number of output notes created in this transaction.
#!
#! Inputs: [pad(16)]
#! Outputs: [num_output_notes, pad(15)]
#!
#! Where:
#! - num_output_notes is the number of output notes created in this transaction so far.
#!
#! Invocation: dynexec
export.tx_get_num_output_notes
# get the number of input notes
exec.tx::get_num_output_notes
# => [num_output_notes, pad(16)]
# truncate the stack
swap drop
# => [num_output_notes, pad(15)]
end
#! Returns the block commitment of the reference block.
#!
#! Inputs: [pad(16)]
#! Outputs: [BLOCK_COMMITMENT, pad(12)]
#!
#! Where:
#! - BLOCK_COMMITMENT is the commitment of the transaction reference block.
#!
#! Invocation: dynexec
export.tx_get_block_commitment
exec.tx::get_block_commitment
# => [BLOCK_COMMITMENT, pad(16)]
# truncate the stack
swapw dropw
# => [BLOCK_COMMITMENT, pad(12)]
end
#! Returns the block number of the transaction reference block at the time of transaction execution.
#!
#! Inputs: [pad(16)]
#! Outputs: [num, pad(15)]
#!
#! Where:
#! - num is the transaction reference block number.
#!
#! Invocation: dynexec
export.tx_get_block_number
# get the block number
exec.tx::get_block_number
# => [num, pad(16)]
# truncate the stack
swap drop
# => [num, pad(15)]
end
#! Returns the block timestamp of the reference block for this transaction.
#!
#! Inputs: [pad(16)]
#! Outputs: [timestamp, pad(15)]
#!
#! Where:
#! - timestamp is the timestamp of the reference block for this transaction.
export.tx_get_block_timestamp
# get the transaction reference block timestamp
exec.tx::get_block_timestamp
# => [timestamp, pad(16)]
# truncate the stack
swap drop
# => [timestamp, pad(15)]
end
#! Starts a foreign account context.
#!
#! This allows calling procedures on an account different from the native account. It loads the
#! foreign account into memory, unless already loaded. It pushes the foreign account onto the
#! account stack, which makes the foreign account the active account.
#!
#! Inputs:
#! Operand stack: [foreign_account_id_prefix, foreign_account_id_suffix, pad(14)]
#! Advice map: {
#! FOREIGN_ACCOUNT_ID: [[foreign_account_id_suffix, foreign_account_id_prefix, 0, account_nonce],
#! VAULT_ROOT, STORAGE_ROOT, CODE_ROOT],
#! STORAGE_ROOT: [[STORAGE_SLOT_DATA]],
#! CODE_ROOT: [num_procs, [ACCOUNT_PROCEDURE_DATA]]
#! }
#! Outputs:
#! Operand stack: [pad(16)]
#!
#! Where:
#! - foreign_account_id_{prefix,suffix} are the prefix and suffix felts of the ID of the foreign
#! account whose procedure is going to be executed.
#! - FOREIGN_ACCOUNT_ID is the word constructed from the foreign_account_id as follows:
#! [foreign_account_id_suffix, foreign_account_id_prefix, 0, 0].
#! - account_nonce is the nonce of the foreign account.
#! - VAULT_ROOT is the commitment of the foreign account's vault.
#! - STORAGE_ROOT is the commitment of the foreign account's storage.
#! - STORAGE_SLOT_DATA is the data contained in the storage slot which is constructed as follows:
#! [SLOT_VALUE, slot_type, 0, 0, 0].
#! - CODE_COMMITMENT is the commitment of the foreign account's code.
#! - ACCOUNT_PROCEDURE_DATA is the information about account procedure which is constructed as
#! follows: [PROCEDURE_MAST_ROOT, storage_offset, 0, 0, 0].
#!
#! Panics if:
#! - foreign context is created against the native account.
#!
#! Invocation: dynexec
export.tx_start_foreign_context
# get the memory address and a flag whether this account was already loaded.
exec.account::get_account_data_ptr
# OS => [was_loaded, ptr, foreign_account_id_prefix, foreign_account_id_suffix, pad(14)]
if.true
exec.memory::push_ptr_to_account_stack drop drop
# OS => [pad(16)]
else
exec.memory::push_ptr_to_account_stack
# OS => [foreign_account_id_prefix, foreign_account_id_suffix, pad(14)]
# load the advice data into the active account memory section
exec.account::load_foreign_account
end
# make sure that the state of the loaded foreign account corresponds to this commitment in the
# account database
exec.account::validate_active_foreign_account
# => [pad(16)]
end
#! Ends a foreign account context.
#!
#! This pops the top of the account stack, making the previous account the active account.
#!
#! Inputs: [pad(16)]
#! Outputs: [pad(16)]
#!
#! Panics if:
#! - the active account is the native account.
#!
#! Invocation: dynexec
export.tx_end_foreign_context
exec.memory::pop_ptr_from_account_stack
# => [pad(16)]
end
#! Updates the transaction expiration block delta.
#!
#! Once set, the delta can be decreased but not increased.
#!
#! The input block height delta is added to the reference block in order to output an upper limit
#! up until which the transaction will be considered valid (not expired).
#!
#! Inputs: [block_height_delta, pad(15)]
#! Output: [pad(16)]
#!
#! Where:
#! - block_height_delta is the desired expiration time delta (1 to 0xFFFF).
#!
#! Invocation: dynexec
export.tx_update_expiration_block_delta
exec.tx::update_expiration_block_delta
# => [pad(16)]
end
#! Gets the transaction expiration delta.
#!
#! Inputs: [pad(16)]
#! Output: [block_height_delta, pad(15)]
#!
#! Where:
#! - block_height_delta is the stored expiration time delta (1 to 0xFFFF).
#!
#! Invocation: dynexec
export.tx_get_expiration_delta
exec.tx::get_expiration_delta
# => [block_height_delta, pad(16)]
# truncate the stack
swap drop
# => [block_height_delta, pad(15)]
end
#! Executes a kernel procedure specified by its offset.
#!
#! Inputs: [procedure_offset, <procedure_inputs>, <pad>]
#! Outputs: [<procedure_outputs>, <pad>]
#!
#! Where:
#! - procedure_offset is an offset of the kernel procedure, specified in the
#! `miden/kernel_proc_offsets.masm` file.
#! - procedure_inputs are inputs of the procedure to be executed, which is specified by the
#! procedure_offset. Note that the length of this inputs cannot exceed 15 elements, since the
#! first element on the stack will be occupied by the memory pointer to the procedure root.
#! - procedure_outputs are the outputs of the procedure to be executed.
#!
#! Panics if:
#! - the provided procedure offset exceeds the number of kernel procedures.
#!
#! Invocation: syscall
export.exec_kernel_proc
# check that the provided procedure offset is within expected bounds
dup exec.memory::get_num_kernel_procedures
lt assert.err=ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS
# => [procedure_offset, <procedure_inputs>, <pad>]
# compute the memory pointer at which desired procedure is stored
mul.4 exec.memory::get_kernel_procedures_ptr add
# => [procedure_pointer, <procedure_inputs>, <pad>]
# execute loaded procedure
dynexec
# => [<procedure_outputs>, <pad>]
end
# HELPER PROCEDURES
# =================================================================================================
#! Returns the memory pointer to the input note, depending on whether the requested note is current
#! or it was requested by index.
#!
#! If the pointer to the active note was requested, but no note is being executed, 0
#! is returned.
#!
#! Inputs: [is_active_note, note_index]
#! Outputs: [input_note_ptr]
#!
#! Where:
#! - is_active_note is the boolean flag indicating whether we should return requested data from
#! the active note or from the note with the specified index.
#! - note_index is the index of the input note whose data should be returned. Notice that if
#! is_active_note is 1, note_index is ignored.
#! - input_note_ptr is the pointer to the correct input note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of input notes.
proc.get_requested_note_ptr
# get the memory pointer to the note with the specified index and verify it is valid
swap exec.input_note::get_input_note_ptr swap
# => [is_active_note, indexed_input_note_ptr]
# get the memory pointer to the currently processed input note
exec.memory::get_active_input_note_ptr swap
# => [is_active_note, active_input_note_ptr, indexed_input_note_ptr]
# If is_active_note flag is true (active note processing case), active_input_note_ptr remains
# on the stack. If it is false, indexed_input_note_ptr remains instead.
cdrop
# => [input_note_ptr]
end