Transaction Contexts

Miden assembly program execution can span multiple isolated contexts. An execution context defines its own memory space which is not accessible from other execution contexts. Note scripts cannot directly write into account data. This should be possible if only if the account exposes respective functions.

The kernel program always starts executing in a root context. Thus, the prologue sets the memory for the root context. To move execution into a different context, we can invoke a procedure using the call or dyncall instruction. In fact, any time we invoke a procedure using the call instruction, the procedure is executed in a new context.

While executing in a note, account, or tx script context, we can request to execute some procedures in the kernel context, which is where all necessary information was stored during the prologue. Switching to the kernle context can be done via the syscall instruction. The set of procedures which can be invoked via the syscall instruction is limited by the transaction kernel API. Once the procedure call via syscall returns, the execution moves back to the note, account, or tx script from which it was invoked.


\


\

The above diagram shows different context switches in a simple transaction. In this example, an account consumes a P2ID note and receives the asset into its vault. As with in any MASM program, the transaction kernel program starts in the root context. It executes the Prologue and stores all necessary information into the root memory.

The next step, note processing, starts with a dyncall and in doing so, invoking the note script. This command moves execution into a different context (1). In this new context, the note has no access to the kernel memory. After a successful ID check, which changes back to the kernel context twice to get the note inputs and the account id, the script executes the add_note_assets_to_account procedure.

# Pay-to-ID script: adds all assets from the note to the account, assuming ID of the account
# matches target account ID specified by the note inputs.
# ...
begin

    ... <check correct ID>

    exec.add_note_assets_to_account
    # => [...]
end

The procedure cannot simply add assets to the account, because it is executed in a note context. Therefore, it needs to call the account interface. And in doing so, it moves execution into a second context - Account context - isolated from the note context (2).

#! Helper procedure to add all assets of a note to an account.
#! ...
proc.add_note_assets_to_account
    ...

    while.true
        ...

        # load the asset and add it to the account
        mem_loadw call.wallet::receive_asset
        # => [ASSET, ptr, end_ptr, ...]
        ...
    end
    ...
end

The wallet smart contract provides an interface for accounts to recieve and send assets. In this new context, the wallet calls the add_asset procedure of the account API.

export.receive_asset
    exec.account::add_asset
    ...
end

The account API exposes procedures to manage accounts. This particular procedure that was called by the wallet invokes a syscall to return back to the root context (3), where the account vault is stored in memory (see Prologue). syscall can incoke all procedures defined in the Kernel API.

#! Add the specified asset to the vault.
#! ...
export.add_asset
    syscall.account_vault_add_asset
end

Now, the asset can be safely added to the vault within the kernel context and the note successfully be processed.

Transaction Procedures

There are user-facing procedures and kernel procedures. Users don't directly invoke kernel procedures, but indirectly via account code, note or transaction scripts. In that case, kernel procedures can only be invoked by a syscall instruction which always executes in the kernel context.

User-facing Procedures (APIs)

These procedures can be used to create smart contract / account code, note scripts or account scripts. They basically serve as an API for the underlying kernel procedures. If a procedure can be called in the current context an exec is sufficient, otherwise if being the wrong context procedures must be invoked by call. Users will never need to invoke syscall procedures themselves.

Note: If capitalized, a variable represents a Word, e.g., ACCT_HASH consists of four Felts. If lowercase, the variable is represented by a single Felt.

Account

To import the account procedures set use.miden::account at the beginning of the file. Any procedure that changes the account state, can only be invoked in the account context and not by note or transaction scripts. All procedures invoke syscall to the kernel API and some are restricted by the kernel procedure exec.authenticate_account_origin, which fails if the parent context is not the executing account.

Procedure nameStackOutputContextDescription
get_id[][acct_id]account, note
ViewReturns the account id. acct_id is the account id.
get_nonce[][nonce]account, note
ViewReturns the account nonce. nonce is the account nonce.
get_initial_hash[][H]account, note
ViewReturns the initial account hash. H is the initial account hash.
get_current_hash[][ACCT_HASH]account, note
ViewComputes and returns the account hash from account data stored in memory. ACCT_HASH is the hash of the account data.
incr_nonce[value][]account
ViewIncrements the account nonce by the provided value. value is the value to increment the nonce by. value can be at most 2^32 - 1 otherwise this procedure panics.
get_item[index][VALUE]account, note
ViewGets an item from the account storage. Panics if the index is out of bounds. index is the index of the item to get. VALUE is the value of the item.
set_item[index, V'][R', V]account
ViewSets an item in the account storage. Panics if the index is out of bounds. index is the index of the item to set. V' is the value to set. V is the previous value of the item. R' is the new storage root.
set_code[CODE_ROOT][]account
ViewSets the code of the account the transaction is being executed against. This procedure can only be executed on regular accounts with updatable code. Otherwise, this procedure fails. CODE_ROOT is the hash of the code to set.
get_balance[faucet_id][balance]account, note
ViewReturns the balance of a fungible asset associated with a faucet_id. Panics if the asset is not a fungible asset. faucet_id is the faucet id of the fungible asset of interest. balance is the vault balance of the fungible asset.
has_non_fungible_asset[ASSET][has_asset]account, note
ViewReturns a boolean indicating whether the non-fungible asset is present in the vault. Panics if the ASSET is a fungible asset. ASSET is the non-fungible asset of interest. has_asset is a boolean indicating whether the account vault has the asset of interest.
add_asset[ASSET][ASSET']account
ViewAdd the specified asset to the vault. Panics under various conditions. 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.
remove_asset[ASSET][ASSET]account
ViewRemove the specified asset from the vault. Panics under various conditions. ASSET is the asset to remove from the vault.
get_vault_commitment[][COM]account, note
ViewReturns a commitment to the account vault. COM is a commitment to the account vault.

Note

To import the note procedures set use.miden::note at the beginning of the file. All procedures are restricted to the note context.

Procedure nameInputsOutputsContextDescription
get_assets[dest_ptr][num_assets, dest_ptr]note
ViewWrites the assets of the currently executing note into memory starting at the specified address. dest_ptr is the memory address to write the assets. num_assets is the number of assets in the currently executing note.
get_inputs[dest_ptr][dest_ptr]note
ViewWrites the inputs of the currently executed note into memory starting at the specified address. dest_ptr is the memory address to write the inputs.
get_sender[][sender]note
ViewReturns the sender of the note currently being processed. Panics if a note is not being processed. sender is the sender of the note currently being processed.

Tx

To import the transaction procedures set use.miden::tx at the beginning of the file. Only the create_note procedure is restricted to the account context.

Procedure nameInputsOutputsContextDescription
get_block_number[][num]account, note
ViewReturns the block number of the last known block at the time of transaction execution. num is the last known block number.
get_block_hash[][H]account, note
ViewReturns the block hash of the last known block at the time of transaction execution. H is the last known block hash.
get_input_notes_hash[][COM]account, note
ViewReturns the input notes hash. This is computed as a sequential hash of (nullifier, script_root) tuples over all input notes. COM is the input notes hash.
get_output_notes_hash[0, 0, 0, 0][COM]account, note
ViewReturns the output notes hash. This is computed as a sequential hash of (note_hash, note_metadata) tuples over all output notes. COM is the output notes hash.
create_note[ASSET, tag, RECIPIENT][ptr]account
ViewCreates a new note and returns a pointer to the memory address at which the note is stored. ASSET is the asset to be included in the note. tag is the tag to be included in the note. RECIPIENT is the recipient of the note. ptr is the pointer to the memory address at which the note is stored.

Asset

To import the asset procedures set use.miden::asset at the beginning of the file. These procedures can only be called by faucet accounts.

Procedure nameStackOutputContextDescription
build_fungible_asset[faucet_id, amount][ASSET]faucet
ViewBuilds a fungible asset for the specified fungible faucet and amount. faucet_id is the faucet to create the asset for. amount is the amount of the asset to create. ASSET is the built fungible asset.
create_fungible_asset[amount][ASSET]faucet
ViewCreates a fungible asset for the faucet the transaction is being executed against. amount is the amount of the asset to create. ASSET is the created fungible asset.
build_non_fungible_asset[faucet_id, DATA_HASH][ASSET]faucet
ViewBuilds a non-fungible asset for the specified non-fungible faucet and DATA_HASH. faucet_id is the faucet to create the asset for. DATA_HASH is the data hash of the non-fungible asset to build. ASSET is the built non-fungible asset.
create_non_fungible_asset[DATA_HASH][ASSET]faucet
ViewCreates a non-fungible asset for the faucet the transaction is being executed against. DATA_HASH is the data hash of the non-fungible asset to create. ASSET is the created non-fungible asset.

Faucet

To import the faucet procedures set use.miden::faucet at the beginning of the file.

Procedure nameStackOutputsContextDescription
mint[ASSET][ASSET]faucet
ViewMint an asset from the faucet the transaction is being executed against. Panics under various conditions. ASSET is the asset that was minted.
burn[ASSET][ASSET]faucet
ViewBurn an asset from the faucet the transaction is being executed against. Panics under various conditions. ASSET is the asset that was burned.
get_total_issuance[][total_issuance]faucet
ViewReturns 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. total_issuance is the total issuance of the fungible faucet the transaction is being executed against.

Kernel Procedures

WIP - we will add those later.