use $kernel::asset
use $kernel::account
use $kernel::account_delta
use $kernel::account_id
use $kernel::faucet
use $kernel::input_note
use $kernel::memory
use $kernel::output_note
use $kernel::tx
# use $kernel::types::AccountId
use $kernel::memory::UPCOMING_FOREIGN_PROCEDURE_PTR
use $kernel::memory::UPCOMING_FOREIGN_PROC_INPUT_VALUE_15_PTR
use miden::core::word
pub type AccountId = struct { prefix: felt, suffix: felt }
# 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_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_STORAGE_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note storage 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"
const ERR_FOREIGN_ACCOUNT_PROCEDURE_ROOT_IS_ZERO="root of the provided foreign procedure equals zero indicating that tx_prepare_fpi was not called"
const ERR_FOREIGN_ACCOUNT_ID_IS_ZERO="ID of the provided foreign account equals zero indicating that tx_prepare_fpi was not called"
# AUTHENTICATION
# =================================================================================================
#! Authenticates that the invocation of a kernel procedure originates from the account context.
#!
#! Inputs: []
#! Outputs: []
#!
#! 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
# => []
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
pub proc 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
pub proc 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
pub proc 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_suffix, account_id_prefix, 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
pub proc account_get_id
# get the native account ID
exec.memory::get_native_account_id
# => [native_account_id_suffix, native_account_id_prefix, is_native, pad(15)]
# get the active account ID
exec.account::get_id
# => [
# active_account_id_suffix, active_account_id_prefix,
# native_account_id_suffix, native_account_id_prefix,
# is_native, pad(15)
# ]
# prepare the stack for the first cdrop
movup.2 dup.4
# => [
# is_native, native_account_id_suffix, active_account_id_suffix,
# active_account_id_prefix, native_account_id_prefix, is_native, pad(15)
# ]
# drop the suffix corresponding to the is_native flag
cdrop
# => [account_id_suffix, active_account_id_prefix, native_account_id_prefix, is_native, pad(15)]
# prepare the stack for the second cdrop
movdn.3 swap movup.2
# => [is_native, native_account_id_prefix, active_account_id_prefix, account_id_suffix, pad(15)]
# drop the prefix corresponding to the is_native flag
cdrop
# => [account_id_prefix, account_id_suffix, pad(15)]
# rearrange the ID parts and truncate the stack
swap movup.2 drop
# => [account_id_suffix, account_id_prefix, 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
pub proc 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
pub proc 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
pub proc account_get_code_commitment
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [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
pub proc 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
pub proc account_compute_storage_commitment
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [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: [slot_id_suffix, slot_id_prefix, pad(14)]
#! Outputs: [VALUE, pad(12)]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#! the first two felts of the hashed slot name.
#! - VALUE is the value of the item.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
#!
#! Invocation: dynexec
pub proc account_get_item
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [slot_id_suffix, slot_id_prefix, pad(14)]
# 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: [slot_id_suffix, slot_id_prefix, VALUE, pad(10)]
#! Outputs: [OLD_VALUE, pad(12)]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#! the first two felts of the hashed slot name.
#! - VALUE is the value to set.
#! - OLD_VALUE is the previous value of the item.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
#! - the invocation of this procedure does not originate from the native account.
#!
#! Invocation: dynexec
pub proc account_set_item
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [slot_id_suffix, slot_id_prefix, VALUE, pad(10)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [slot_id_suffix, slot_id_prefix, VALUE, pad(10)]
# 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 account
#! storage slot identified by the slot ID.
#!
#! Inputs: [slot_id_suffix, slot_id_prefix, KEY, pad(10)]
#! Outputs: [VALUE, pad(12)]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#! the first two felts of the hashed slot name.
#! - VALUE is the value of the map item at KEY.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
#! - the requested storage slot type is not map.
#!
#! Invocation: dynexec
pub proc account_get_map_item
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [slot_id_suffix, slot_id_prefix, KEY, pad(10)]
# 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: [slot_id_suffix, slot_id_prefix, pad(14)]
#! Outputs: [INIT_VALUE, pad(12)]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#! the first two felts of the hashed slot name.
#! - INIT_VALUE is the initial value of the item at the beginning of the transaction.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
#!
#! Invocation: dynexec
pub proc account_get_initial_item
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [slot_id_suffix, slot_id_prefix, pad(14)]
# 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
#! account storage slot identified by the slot ID at the beginning of the transaction.
#!
#! Inputs: [slot_id_suffix, slot_id_prefix, KEY, pad(10)]
#! Outputs: [INIT_VALUE, pad(12)]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#! the first two felts of the hashed slot name.
#! - the slot must point to the root of the storage map.
#! - INIT_VALUE is the initial value of the map item at KEY at the beginning of the transaction.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
#! - the requested storage slot type is not map.
#!
#! Invocation: dynexec
pub proc account_get_initial_map_item
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [slot_id_suffix, slot_id_prefix, KEY, pad(10)]
# 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: [slot_id_suffix, slot_id_prefix, KEY, NEW_VALUE, pad(6)]
#! Outputs: [OLD_VALUE, pad(12)]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#! the first two felts of the hashed slot name.
#! - the slot must point to the root of the storage map.
#! - 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.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
#! - 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
pub proc account_set_map_item
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [slot_id_suffix, slot_id_prefix, KEY, NEW_VALUE, pad(6)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [slot_id_suffix, slot_id_prefix, KEY, NEW_VALUE, pad(6)]
# set the new map item
exec.account::set_map_item
# => [OLD_VALUE, pad(16)]
# truncate the stack
swapw dropw
# => [OLD_VALUE, pad(12)]
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
pub proc 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
pub proc 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_KEY, ASSET_VALUE, pad(8)]
#! Outputs: [ASSET_VALUE', pad(12)]
#!
#! Where:
#! - ASSET_KEY is the vault key of the asset that is added to the vault.
#! - ASSET_VALUE is the value of the asset to add to the vault.
#! - ASSET_VALUE' final asset in the account vault defined as follows:
#! - If ASSET_VALUE is a non-fungible asset, then ASSET_VALUE' is the same as ASSET_VALUE.
#! - If ASSET_VALUE is a fungible asset, then ASSET_VALUE' is the total fungible asset in the account vault
#! after ASSET_VALUE 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
pub proc account_add_asset
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [ASSET_KEY, ASSET_VALUE, pad(8)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [ASSET_KEY, ASSET_VALUE, pad(8)]
# add the specified asset to the account vault, emitting the corresponding events
exec.account::add_asset_to_vault
# => [ASSET_VALUE', pad(12)]
end
#! Removes the specified asset from the vault and returns the remaining asset value.
#!
#! Inputs: [ASSET_KEY, ASSET_VALUE, pad(8)]
#! Outputs: [REMAINING_ASSET_VALUE, pad(12)]
#!
#! Where:
#! - ASSET_KEY is the vault key of the asset to remove from the vault.
#! - ASSET_VALUE is the value of the asset to remove from the vault.
#! - REMAINING_ASSET_VALUE is the value of the asset remaining in the vault after removal.
#!
#! 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
pub proc account_remove_asset
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [ASSET_KEY, ASSET_VALUE, pad(8)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [ASSET_KEY, ASSET_VALUE, pad(8)]
# remove the specified asset from the account vault, emitting the corresponding events
exec.account::remove_asset_from_vault
# => [REMAINING_ASSET_VALUE, pad(12)]
end
#! Returns the asset associated with the provided asset vault key in the active account's vault.
#!
#! Inputs: [ASSET_KEY, pad(12)]
#! Outputs: [ASSET_VALUE, pad(12)]
#!
#! Where:
#! - ASSET_KEY is the asset vault key of the asset to fetch.
#! - ASSET_VALUE is the value of the asset from the vault, which can be the EMPTY_WORD if it isn't
#! present.
#!
#! Invocation: dynexec
pub proc account_get_asset
exec.asset::validate_key
# => [ASSET_KEY, pad(12)]
exec.account::get_asset
# => [ASSET_VALUE, pad(12)]
end
#! Returns the asset associated with the provided asset vault key in the active account's vault at
#! the beginning of the transaction.
#!
#! Inputs: [ASSET_KEY, pad(12)]
#! Outputs: [ASSET_VALUE, pad(12)]
#!
#! Where:
#! - ASSET_KEY is the asset vault key of the asset to fetch.
#! - ASSET_VALUE is the value of the asset from the vault, which can be the EMPTY_WORD if it isn't present.
#!
#! Invocation: dynexec
pub proc account_get_initial_asset
exec.asset::validate_key
# => [ASSET_KEY, pad(12)]
exec.account::get_initial_asset
# => [ASSET_VALUE, pad(12)]
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
pub proc 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
pub proc 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
pub proc account_get_procedure_root
# get the procedure root
exec.account::get_procedure_root
# => [PROC_ROOT, pad(15)]
# truncate the stack
movup.4 drop movup.4 drop 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
pub proc 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_KEY, ASSET_VALUE, pad(8)]
#! Outputs: [NEW_ASSET_VALUE, pad(12)]
#!
#! Where:
#! - ASSET_KEY is the vault key of the asset to mint.
#! - ASSET_VALUE is the value of the asset that was minted.
#! - NEW_ASSET_VALUE is:
#! - For fungible assets: the ASSET_VALUE merged with the existing vault asset value, if any.
#! - For non-fungible assets: identical to ASSET_VALUE.
#!
#! 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
pub proc faucet_mint_asset
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [ASSET_KEY, ASSET_VALUE, pad(8)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [ASSET_KEY, ASSET_VALUE, pad(8)]
# mint the asset
exec.faucet::mint
# => [NEW_ASSET_VALUE, pad(12)]
end
#! Burn an asset from the faucet the transaction is being executed against.
#!
#! Inputs: [ASSET_KEY, ASSET_VALUE, pad(8)]
#! Outputs: [pad(16)]
#!
#! Where:
#! - ASSET_KEY is the vault key of the asset to burn.
#! - ASSET_VALUE is the value of the asset to burn.
#!
#! 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
pub proc faucet_burn_asset
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [ASSET_KEY, ASSET_VALUE, pad(8)]
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [ASSET_KEY, ASSET_VALUE, pad(8)]
# burn the asset
exec.faucet::burn
# => [pad(16)]
end
#! Returns whether the active account defines callbacks.
#!
#! Inputs: [pad(16)]
#! Outputs: [has_callbacks, pad(15)]
#!
#! Where:
#! - has_callbacks is 1 if the account defines callbacks, 0 otherwise.
#!
#! Invocation: dynexec
pub proc faucet_has_callbacks
exec.account::has_callbacks
# => [has_callbacks, pad(16)]
# truncate the stack
swap drop
# => [has_callbacks, 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 storage
#! from incorrect context).
#!
#! Invocation: dynexec
pub proc 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, storage, 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 storage
#! from incorrect context).
#!
#! Invocation: dynexec
pub proc 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: [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)]
#!
#! 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_HEADER is the metadata header of the specified input note.
#! - NOTE_ATTACHMENT is the attachment 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 storage
#! from incorrect context).
#!
#! Invocation: dynexec
pub proc 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)]
# make stack truncation at the end of the procedure easier
push.0 swap
# => [input_note_ptr, pad(16)]
# get the metadata
dup exec.memory::get_input_note_metadata_header
# => [METADATA_HEADER, input_note_ptr, pad(16)]
# get the attachment
movup.4 exec.memory::get_input_note_attachment
# => [NOTE_ATTACHMENT, METADATA_HEADER, pad(16)]
# truncate the stack
swapdw dropw dropw
# => [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)]
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 storage
#! from incorrect context).
#!
#! Invocation: dynexec
pub proc 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 storage commitment and length of the specified input note.
#!
#! Inputs: [is_active_note, note_index, pad(14)]
#! Outputs: [NOTE_STORAGE_COMMITMENT, num_storage_items, pad(11)]
#!
#! Where:
#! - is_active_note is the boolean flag indicating whether we should return the storage 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_STORAGE_COMMITMENT is the storage commitment of the specified input note.
#! - num_storage_items is the number of storage items 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 storage
#! from incorrect context).
#!
#! Invocation: dynexec
pub proc input_note_get_storage_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_STORAGE_WHILE_NO_NOTE_BEING_PROCESSED
# => [input_note_ptr, pad(15)]
# get the note's number of storage items
dup exec.memory::get_input_note_num_storage_items swap
# => [input_note_ptr, num_storage_items, pad(16)]
# get the storage commitment
exec.memory::get_input_note_storage_commitment
# => [NOTE_STORAGE_COMMITMENT, num_storage_items, pad(16)]
# truncate the stack
repeat.5
movup.5 drop
end
# => [NOTE_STORAGE_COMMITMENT, num_storage_items, 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 storage 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 storage
#! from incorrect context).
#!
#! Invocation: dynexec
pub proc 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, note_type, RECIPIENT, pad(10)]
#! Outputs: [note_idx, pad(15)]
#!
#! Where:
#! - tag is the tag to be included in the note.
#! - note_type is the note storage type.
#! - RECIPIENT is the recipient of the note.
#! - note_idx is the index of the created note.
#!
#! Invocation: dynexec
pub proc output_note_create
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [tag, note_type, RECIPIENT, pad(10)]
exec.output_note::create
# => [note_idx, pad(15)]
end
#! Adds the asset to the note specified by the index.
#!
#! Inputs: [ASSET_KEY, ASSET_VALUE, note_idx, pad(7)]
#! Outputs: [pad(16)]
#!
#! Where:
#! - note_idx is the index of the note to which the asset is added.
#! - ASSET_KEY is the vault key of the asset to add.
#! - ASSET_VALUE is the value of the asset to add.
#!
#! Panics if:
#! - the procedure is called when the active account is not the native one.
#!
#! Invocation: dynexec
pub proc output_note_add_asset
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [ASSET_KEY, ASSET_VALUE, note_idx, pad(7)]
exec.output_note::add_asset
# => [pad(16)]
end
#! Sets the attachment of the note specified by the index.
#!
#! Inputs: [note_idx, attachment_scheme, attachment_kind, ATTACHMENT, pad(9)]
#! Outputs: [pad(16)]
#!
#! Where:
#! - note_idx is the index of the note on which the attachment is set.
#! - attachment_scheme is the user-defined scheme of the attachment.
#! - attachment_kind is the kind of the attachment content.
#! - ATTACHMENT is the attachment to be set.
#!
#! Panics if:
#! - the procedure is called when the active account is not the native one.
#! - the note index points to a non-existent output note.
#! - the attachment kind or scheme does not fit into a u32.
#! - the attachment kind is an unknown variant.
#!
#! Invocation: dynexec
pub proc output_note_set_attachment
# check that this procedure was executed against the native account
exec.memory::assert_native_account
# => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT, pad(9)]
exec.output_note::set_attachment
# => [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
pub proc 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, storage, the serial number.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of output notes.
#!
#! Invocation: dynexec
pub proc 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: [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)]
#!
#! Where:
#! - note_index is the index of the output note whose metadata should be returned.
#! - METADATA_HEADER is the metadata header of the specified output note.
#! - NOTE_ATTACHMENT is the attachment of the specified output note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of output notes.
#!
#! Invocation: dynexec
pub proc 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)]
# make stack truncation at the end of the procedure easier
push.0 swap
# => [note_ptr, pad(16)]
# get the metadata
dup exec.memory::get_output_note_metadata_header
# => [METADATA_HEADER, note_ptr, pad(16)]
# get the attachment
movup.4 exec.memory::get_output_note_attachment
# => [NOTE_ATTACHMENT, METADATA_HEADER, pad(16)]
# truncate the stack
swapdw dropw dropw
# => [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)]
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
pub proc 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
pub proc 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
pub proc 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
pub proc 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
pub proc 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
pub proc 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.
pub proc 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
#! Saves the foreign account ID, foreign procedure root, and the 16th (last) element of the foreign
#! procedure inputs to the memory.
#!
#! To work around the 15 value limitation of the `exec_kernel_proc` kernel procedure we store the
#! 16th value of the foreign procedure inputs to the kernel memory: this allows to FPI any account
#! procedure, even if it has 16 input values.
#!
#! Inputs: [foreign_account_id_suffix, foreign_account_id_prefix, FOREIGN_PROC_ROOT, foreign_proc_input_value_15, pad(9)]
#! Outputs: [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_PROC_ROOT is the root of the foreign procedure which will be executed during the FPI
#! call.
#! - foreign_proc_input_value_15 is the 16th (last) value of the foreign procedure inputs.
#!
#! Panics if:
#! - the provided foreign account ID is invalid.
#!
#! Invocation: dynexec
pub proc tx_prepare_fpi(foreign_account_id: AccountId, foreign_proc_root: word, foreign_procedure_input_15: felt)
# validate the provided foreign account ID
dup.1 dup.1 exec.account_id::validate
# => [foreign_account_id_suffix, foreign_account_id_prefix, FOREIGN_PROC_ROOT, foreign_proc_input_value_15, pad(9)]
# store the foreign account ID
exec.memory::set_fpi_account_id
# => [FOREIGN_PROC_ROOT, foreign_proc_input_value_15, pad(11)]
# store the foreign procedure root
exec.memory::set_fpi_procedure_root dropw
# => [foreign_proc_input_value_15, pad(15)]
# store the 16th value of the foreign procedure inputs
mem_store.UPCOMING_FOREIGN_PROC_INPUT_VALUE_15_PTR
# => [pad(16)]
end
#! Executes the procedure against the foreign account.
#!
#! This procedure should be executed after the `tx_prepare_fpi`, so it is expected for the foreign
#! account ID and foreign procedure root to be already loaded to the kernel memory.
#!
#! Inputs: [foreign_procedure_inputs(15), pad]
#! Outputs: [foreign_procedure_outputs(16)]
#!
#! Where:
#! - foreign_procedure_inputs are the inputs to the foreign procedure padded to 15 felts. Notice
#! that the 16th (last) element of the inputs is not here and stored in the memory: it will be
#! loaded during the setup.
#! - foreign_procedure_outputs are the outputs of the foreign procedure padded to 16 felts.
#!
#! Panics if:
#! - foreign context is created against the native account.
#! - the ID of the foreign account loaded from the kernel memory is zero (ID was not set).
#! - the root of the foreign procedure loaded from the kernel memory is zero (root was not set).
#!
#! Invocation: dynexec
pub proc tx_exec_foreign_proc
# move up the pad value to the top of the stack so we could drop it later
movup.15
# => [pad, foreign_procedure_inputs(15)]
# load the 16th foreign procedure input value onto the stack
mem_load.UPCOMING_FOREIGN_PROC_INPUT_VALUE_15_PTR
# => [foreign_proc_input_value_15, pad, foreign_procedure_inputs(15)]
# drop the excess pad and move the 16th inputs value to its 15th position
swap drop movdn.15
# => [foreign_procedure_inputs(16)]
# load the ID of the foreign account onto the stack
exec.memory::get_fpi_account_id
# => [foreign_account_id_suffix, foreign_account_id_prefix, foreign_procedure_inputs(16)]
# check that foreign account ID is not equal zero
dup.1 eq.0 dup.1 eq.0 and not assert.err=ERR_FOREIGN_ACCOUNT_ID_IS_ZERO
# => [foreign_account_id_suffix, foreign_account_id_prefix, foreign_procedure_inputs(16)]
# load the foreign account to the memory
exec.tx::start_foreign_context
# => [foreign_procedure_inputs(16)]
# get the pointer to the foreign procedure root to perform the dyncall
push.UPCOMING_FOREIGN_PROCEDURE_PTR
# => [foreign_proc_root_ptr, foreign_procedure_inputs(16)]
# check that the foreign procedure root is not zero
padw mem_loadw_le.UPCOMING_FOREIGN_PROCEDURE_PTR
# => [FOREIGN_PROC_ROOT, foreign_proc_root_ptr, foreign_procedure_inputs(16)]
exec.word::eqz assertz.err=ERR_FOREIGN_ACCOUNT_PROCEDURE_ROOT_IS_ZERO
# => [foreign_proc_root_ptr, foreign_procedure_inputs(16)]
# call the foreign procedure
dyncall
# => [foreign_procedure_outputs(16)]
# end the foreign context
exec.tx::end_foreign_context
# => [foreign_procedure_outputs(16)]
# clear the foreign procedure ID and root in memory
exec.tx::clear_fpi_memory
# => [foreign_procedure_outputs(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
pub proc 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
pub proc 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
pub proc 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