diff --git a/CHANGELOG.md b/CHANGELOG.md index f7626770e3..1edd194eba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ - [BREAKING] Renamed `AccountProcedureInfo` into `AccountProcedureRoot` and remove storage offset and size ([#2162](https://github.com/0xMiden/miden-base/pull/2162)). - [BREAKING] Made `AccountProcedureIndexMap` construction infallible ([#2163](https://github.com/0xMiden/miden-base/pull/2163)). - [BREAKING] Renamed `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot` in ACL auth components for naming consistency ([#2166](https://github.com/0xMiden/miden-base/pull/2166)). -- [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184), [#2191](https://github.com/0xMiden/miden-base/pull/2191), [#2197](https://github.com/0xMiden/miden-base/pull/2197)). +- [BREAKING] Refactor `miden-objects` and `miden-lib` into `miden-protocol` and `miden-standards` ([#2184](https://github.com/0xMiden/miden-base/pull/2184), [#2191](https://github.com/0xMiden/miden-base/pull/2191), [#2197](https://github.com/0xMiden/miden-base/pull/2197), [#2255](https://github.com/0xMiden/miden-base/pull/2255)). - [BREAKING] Migrated to `miden-vm` v0.20 and `miden-crypto` v0.19 ([#2158](https://github.com/0xMiden/miden-base/pull/2158)). - [BREAKING] Refactored `AccountStorageDelta` to use a new `StorageSlotDelta` type ([#2182](https://github.com/0xMiden/miden-base/pull/2182)). - [BREAKING] Removed OLD_MAP_ROOT from being returned when calling [`native_account::set_map_item`](crates/miden-lib/asm/miden/native_account.masm) ([#2194](https://github.com/0xMiden/miden-base/pull/2194)). diff --git a/crates/miden-standards/asm/note_scripts/BURN.masm b/crates/miden-standards/asm/note_scripts/BURN.masm index 9282d5249f..07426b1bc5 100644 --- a/crates/miden-standards/asm/note_scripts/BURN.masm +++ b/crates/miden-standards/asm/note_scripts/BURN.masm @@ -1,28 +1,5 @@ -use miden::standards::faucets +use miden::standards::notes::burn -#! BURN script: burns the asset from the note by calling the faucet's burn procedure. -#! This note can be executed against any faucet account that exposes the faucets::burn procedure -#! (e.g., basic fungible faucet or network fungible faucet). -#! -#! The burn procedure in the faucet already handles all necessary validations including: -#! - Checking that the note contains exactly one asset -#! - Verifying the asset is a fungible asset issued by this faucet -#! - Ensuring the amount to burn doesn't exceed the outstanding supply -#! -#! Requires that the account exposes: -#! - burn procedure (from the faucets interface). -#! -#! Inputs: [ARGS, pad(12)] -#! Outputs: [pad(16)] -#! -#! Panics if: -#! - account does not expose burn procedure. -#! - any of the validations in the burn procedure fail. begin - dropw - # => [pad(16)] - - # Call the faucet's burn procedure which handles all validations - call.faucets::burn - # => [pad(16)] + exec.burn::main end diff --git a/crates/miden-standards/asm/note_scripts/MINT.masm b/crates/miden-standards/asm/note_scripts/MINT.masm index 8d6281b9ee..272fc4ab9a 100644 --- a/crates/miden-standards/asm/note_scripts/MINT.masm +++ b/crates/miden-standards/asm/note_scripts/MINT.masm @@ -1,128 +1,5 @@ -use miden::protocol::active_note -use miden::protocol::note -use miden::standards::faucets::network_fungible->network_faucet +use miden::standards::notes::mint -# CONSTANTS -# ================================================================================================= - -const MINT_NOTE_NUM_INPUTS_PRIVATE=8 -const MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=12 - -const OUTPUT_NOTE_TYPE_PUBLIC=1 -const OUTPUT_NOTE_TYPE_PRIVATE=2 - -const OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=12 -const OUTPUT_PUBLIC_NOTE_INPUTS_LEN_MEM_ADDR=0 - -# ERRORS -# ================================================================================================= -const ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" - -#! Network Faucet MINT script: mints assets by calling the network faucet's distribute function. -#! This note is intended to be executed against a network fungible faucet account. -#! -#! Requires that the account exposes: -#! - miden::contracts::faucets::network_fungible::distribute procedure. -#! -#! Inputs: [ARGS, pad(12)] -#! Outputs: [pad(16)] -#! -#! Note inputs support two modes. Depending on the number of note inputs, -#! a private or public note is created on consumption of the MINT note: -#! -#! Private mode (8 inputs) - creates a private note: -#! - execution_hint: Execution hint for the output note -#! - aux: Auxiliary data for the output note -#! - tag: Note tag for the output note -#! - amount: The amount to mint -#! - RECIPIENT: The recipient digest (4 elements) -#! -#! Public mode (12+ inputs) - creates a public note with variable-length inputs: -#! - execution_hint: Execution hint for the output note -#! - aux: Auxiliary data for the output note -#! - tag: Note tag for the output note -#! - amount: The amount to mint -#! - SCRIPT_ROOT: Script root of the output note (4 elements) -#! - SERIAL_NUM: Serial number of the output note (4 elements) -#! - [INPUTS]: Variable-length inputs for the output note (Vec) -#! The number of output note inputs = num_mint_note_inputs - 12 -#! -#! Panics if: -#! - account does not expose distribute procedure. -#! - the number of inputs is not exactly 8 for private or less than 12 for public output notes. begin - dropw - # => [pad(16)] - # Load note inputs into memory starting at address 0 - push.0 exec.active_note::get_inputs - # => [total_inputs, inputs_ptr, pad(16)] - - dup - # => [num_inputs, num_inputs, inputs_ptr, pad(16)] - - u32assert2.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS - u32gte.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC - # => [is_public_output_note, total_inputs, inputs_ptr, pad(16)] - - if.true - # public output note creation - # => [total_inputs, inputs_ptr, pad(16)] - - movdn.9 drop - # => [EMPTY_WORD, EMPTY_WORD, total_inputs, pad(8)] - - mem_loadw_be.4 - # => [SCRIPT_ROOT, EMPTY_WORD, total_inputs, pad(8)] - - swapw mem_loadw_be.8 - # => [SERIAL_NUM, SCRIPT_ROOT, total_inputs, pad(8)] - - # compute variable length note inputs for the output note - movup.8 sub.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC - # => [num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] - - push.OUTPUT_PUBLIC_NOTE_INPUTS_ADDR - # => [inputs_ptr = 12, num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] - - exec.note::build_recipient - # => [RECIPIENT, pad(12)] - - swapw mem_loadw_be.0 - # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - - push.OUTPUT_NOTE_TYPE_PUBLIC - # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - - movdn.3 - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] - - else - # private output note creation - - eq.MINT_NOTE_NUM_INPUTS_PRIVATE assert.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS drop - # => [inputs_ptr, pad(16)] - - drop - # => [pad(16)] - - mem_loadw_be.4 - # => [RECIPIENT, pad(12)] - - swapw mem_loadw_be.0 - # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - - push.OUTPUT_NOTE_TYPE_PRIVATE - # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] - - movdn.3 - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] - - end - # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] - - call.network_faucet::distribute - # => [pad(17))] - - drop - # => [pad(16)] + exec.mint::main end diff --git a/crates/miden-standards/asm/note_scripts/P2ID.masm b/crates/miden-standards/asm/note_scripts/P2ID.masm index 6e0e455400..d3050ea305 100644 --- a/crates/miden-standards/asm/note_scripts/P2ID.masm +++ b/crates/miden-standards/asm/note_scripts/P2ID.masm @@ -1,53 +1,5 @@ -use miden::protocol::active_account -use miden::protocol::account_id -use miden::protocol::active_note -use miden::standards::wallets::basic->basic_wallet +use miden::standards::notes::p2id -# ERRORS -# ================================================================================================= - -const ERR_P2ID_WRONG_NUMBER_OF_INPUTS="P2ID note expects exactly 2 note inputs" - -const ERR_P2ID_TARGET_ACCT_MISMATCH="P2ID's target account address and transaction address do not match" - -#! 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. -#! -#! Requires that the account exposes: -#! - miden::contracts::wallets::basic::receive_asset procedure. -#! -#! Inputs: [] -#! Outputs: [] -#! -#! Note inputs are assumed to be as follows: -#! - target_account_id is the ID of the account for which the note is intended. -#! -#! Panics if: -#! - Account does not expose miden::contracts::wallets::basic::receive_asset procedure. -#! - Account ID of executing account is not equal to the Account ID specified via note inputs. -#! - The same non-fungible asset already exists in the account. -#! - Adding a fungible asset would result in amount overflow, i.e., the total amount would be -#! greater than 2^63. begin - # store the note inputs to memory starting at address 0 - padw push.0 exec.active_note::get_inputs - # => [num_inputs, inputs_ptr, EMPTY_WORD] - - # make sure the number of inputs is 2 - eq.2 assert.err=ERR_P2ID_WRONG_NUMBER_OF_INPUTS - # => [inputs_ptr, EMPTY_WORD] - - # read the target account ID from the note inputs - mem_loadw_be drop drop - # => [target_account_id_prefix, target_account_id_suffix] - - exec.active_account::get_id - # => [account_id_prefix, account_id_suffix, target_account_id_prefix, target_account_id_suffix, ...] - - # ensure account_id = target_account_id, fails otherwise - exec.account_id::is_equal assert.err=ERR_P2ID_TARGET_ACCT_MISMATCH - # => [] - - exec.basic_wallet::add_assets_to_account - # => [] + exec.p2id::main end diff --git a/crates/miden-standards/asm/note_scripts/P2IDE.masm b/crates/miden-standards/asm/note_scripts/P2IDE.masm index 5a033ba0ef..6d65869eef 100644 --- a/crates/miden-standards/asm/note_scripts/P2IDE.masm +++ b/crates/miden-standards/asm/note_scripts/P2IDE.masm @@ -1,144 +1,5 @@ -use miden::protocol::active_account -use miden::protocol::account_id -use miden::protocol::active_note -use miden::protocol::tx -use miden::standards::wallets::basic->basic_wallet +use miden::standards::notes::p2ide -# ERRORS -# ================================================================================================= - -const ERR_P2IDE_WRONG_NUMBER_OF_INPUTS="P2IDE note expects exactly 4 note inputs" - -const ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER="failed to reclaim P2IDE note because the reclaiming account is not the sender" - -const ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED="failed to reclaim P2IDE note because the reclaim block height is not reached yet" - -const ERR_P2IDE_RECLAIM_DISABLED="P2IDE reclaim is disabled" - -const ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED="failed to consume P2IDE note because the note is still timelocked" - -# HELPER PROCEDURES -# ================================================================================================= - -#! Helper procedure to check if the P2IDE note is unlocked. -#! -#! Inputs: [current_block_height, timelock_block_height] -#! Outputs: [current_block_height] -proc verify_unlocked - dup movdn.2 - # => [current_block_height, timelock_block_height, current_block_height] - - # check timelock_block_height <= current_block_height - lte assert.err=ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED - # => [current_block_height] -end - -#! Helper procedure which adds the note assets to the sender account. -#! -#! Checks if P2IDE reclaim is enabled and if true, if reclaim height has been reached. -#! -#! Inputs: [account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height] -#! Outputs: [] -#! -#! Panics if: -#! - the reclaim of the active note is disabled. -#! - the reclaim block height is not reached yet. -#! - the account attempting to reclaim the note is not the sender account. -proc reclaim_note - # check that the reclaim of the active note is enabled - movup.3 dup neq.0 assert.err=ERR_P2IDE_RECLAIM_DISABLED - # => [reclaim_block_height, account_id_prefix, account_id_suffix, current_block_height] - - # now check that sender is allowed to reclaim, reclaim block height <= current block height - movup.3 - # => [current_block_height, reclaim_block_height, account_id_prefix, account_id_suffix] - - lte assert.err=ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED - # => [account_id_prefix, account_id_suffix] - - # if active account is not the target, we need to ensure it is the sender - exec.active_note::get_sender - # => [sender_account_id_prefix, sender_account_id_suffix, account_id_prefix, account_id_suffix] - - # ensure active account ID = sender account ID - exec.account_id::is_equal assert.err=ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER - # => [] - - # add note assets to account - exec.basic_wallet::add_assets_to_account - # => [] -end - -#! Extended Pay-to-ID note script (Reclaimable & Timelockable) -#! -#! Adds all assets from the note to the account if all of the following conditions are true: -#! - The transaction's reference block number is greater than or equal to the note's timelock block height. -#! - Any of the following conditions is true: -#! - The account ID against which the transaction is executed matches the note's target account id. -#! - The account ID against which the transaction is executed matches the note's sender account id and -#! the transaction's reference block number is greater than or equal to the note's reclaim block height. -#! -#! Requires that the account exposes: -#! - miden::contracts::wallets::basic::receive_asset procedure. -#! -#! Inputs: [] -#! Outputs: [] -#! -#! Note inputs are assumed to be as follows: -#! - target_account_id is the ID of the account for which the note is intended. -#! - reclaim_block_height is the block height at which the note can be reclaimed by the sender. -#! - timelock_block_height is the block height at which the note can be consumed by the target. -#! -#! Panics if: -#! - The account does not expose miden::contracts::wallets::basic::receive_asset procedure. -#! - The note is consumed before the timelock expired, i.e. the transaction's reference block -#! number is less than the timelock block height. -#! - Before reclaim block height: the account ID of the executing account is not equal to the specified -#! account ID. -#! - At and after reclaim block height: the account ID of the executing account is not equal to -#! the specified account ID or sender account ID. -#! - The same non-fungible asset already exists in the account. -#! - Adding a fungible asset would result in an amount overflow, i.e., the total amount would be -#! greater than 2^63. begin - # store the note inputs to memory starting at address 0 - push.0 exec.active_note::get_inputs - # => [num_inputs, inputs_ptr] - - # make sure the number of inputs is 4 - eq.4 assert.err=ERR_P2IDE_WRONG_NUMBER_OF_INPUTS - # => [inputs_ptr] - - # read the reclaim block height, timelock_block_height, and target account ID from the note inputs - mem_loadw_be - # => [timelock_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] - - # read the current block number - exec.tx::get_block_number - # => [current_block_height, timelock_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] - - # fails if note is locked - exec.verify_unlocked - # => [current_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] - - # get active account id - exec.active_account::get_id dup.1 dup.1 - # => [account_id_prefix, account_id_suffix, account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] - - # determine if the active account is the target account - movup.7 movup.7 exec.account_id::is_equal - # => [is_target, account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height] - - if.true - # we can safely consume the note since the active account is the target of the note - dropw exec.basic_wallet::add_assets_to_account - # => [] - - else - # checks if active account is sender and if reclaim is enabled - exec.reclaim_note - # => [] - end - - # => [] + exec.p2ide::main end diff --git a/crates/miden-standards/asm/note_scripts/SWAP.masm b/crates/miden-standards/asm/note_scripts/SWAP.masm index 4c1c2ee8f2..7d4f95b31b 100644 --- a/crates/miden-standards/asm/note_scripts/SWAP.masm +++ b/crates/miden-standards/asm/note_scripts/SWAP.masm @@ -1,112 +1,5 @@ -use miden::protocol::active_note -use miden::protocol::output_note -use miden::standards::wallets::basic->wallet +use miden::standards::notes::swap -# CONSTANTS -# ================================================================================================= - -const SWAP_NOTE_INPUTS_NUMBER=12 - -# ERRORS -# ================================================================================================= - -const ERR_SWAP_WRONG_NUMBER_OF_INPUTS="SWAP script expects exactly 12 note inputs" - -const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset" - -#! Swap script: adds an asset from the note into consumers account and -#! creates a note consumable by note issuer containing requested ASSET. -#! -#! Requires that the account exposes: -#! - miden::contracts::wallets::basic::receive_asset procedure. -#! - miden::contracts::wallets::basic::move_asset_to_note procedure. -#! -#! Inputs: [ARGS] -#! Outputs: [] -#! -#! Note inputs are assumed to be as follows: -#! - REQUESTED_ASSET -#! - PAYBACK_NOTE_RECIPIENT -#! - payback_note_execution_hint -#! - payback_note_type -#! - payback_note_aux -#! - payback_note_tag -#! -#! Panics if: -#! - account does not expose miden::contracts::wallets::basic::receive_asset procedure. -#! - account does not expose miden::contracts::wallets::basic::move_asset_to_note procedure. -#! - account vault does not contain the requested asset. -#! - adding a fungible asset would result in amount overflow, i.e., the total amount would be -#! greater than 2^63. begin - # dropping note args - dropw - # => [] - - # --- create a payback note with the requested asset ---------------- - - # store note inputs into memory starting at address 0 - push.0 exec.active_note::get_inputs - # => [num_inputs, inputs_ptr] - - # make sure the number of inputs is 12 - eq.SWAP_NOTE_INPUTS_NUMBER assert.err=ERR_SWAP_WRONG_NUMBER_OF_INPUTS - # => [inputs_ptr] - - # load REQUESTED_ASSET - mem_loadw_be - # => [REQUESTED_ASSET] - - # load PAYBACK_NOTE_RECIPIENT - padw mem_loadw_be.4 - # => [PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET] - - # load payback P2ID details - padw mem_loadw_be.8 - # => [tag, aux, note_type, execution_hint, PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET] - - # create payback P2ID note - exec.output_note::create - # => [note_idx, REQUESTED_ASSET] - - movdn.4 - # => [REQUESTED_ASSET, note_idx] - - # padding stack with 11 zeros - repeat.11 - push.0 - movdn.5 - end - # => [REQUESTED_ASSET, note_idx, pad(11)] - - # move asset to the note - call.wallet::move_asset_to_note - # => [REQUESTED_ASSET, note_idx, pad(11)] - - dropw drop push.0 - # => [pad(12)] - - # --- move assets from the SWAP note into the account ------------------------- - - # store the number of note assets to memory starting at address 0 - push.0 exec.active_note::get_assets - # => [num_assets, ptr, pad(12)] - - # make sure the number of assets is 1 - assert.err=ERR_SWAP_WRONG_NUMBER_OF_ASSETS - # => [ptr, pad(12)] - - # load the ASSET - mem_loadw_be - # => [ASSET, pad(12)] - - # add the ASSET to the account - call.wallet::receive_asset - # => [pad(16)] - - # clearing the stack of padded 0s - repeat.4 - dropw - end - # => [] + exec.swap::main end diff --git a/crates/miden-standards/asm/standards/notes/burn.masm b/crates/miden-standards/asm/standards/notes/burn.masm new file mode 100644 index 0000000000..20696cc7bc --- /dev/null +++ b/crates/miden-standards/asm/standards/notes/burn.masm @@ -0,0 +1,28 @@ +use miden::standards::faucets + +#! BURN script: burns the asset from the note by calling the faucet's burn procedure. +#! This note can be executed against any faucet account that exposes the faucets::burn procedure +#! (e.g., basic fungible faucet or network fungible faucet). +#! +#! The burn procedure in the faucet already handles all necessary validations including: +#! - Checking that the note contains exactly one asset +#! - Verifying the asset is a fungible asset issued by this faucet +#! - Ensuring the amount to burn doesn't exceed the outstanding supply +#! +#! Requires that the account exposes: +#! - burn procedure (from the faucets interface). +#! +#! Inputs: [ARGS, pad(12)] +#! Outputs: [pad(16)] +#! +#! Panics if: +#! - account does not expose burn procedure. +#! - any of the validations in the burn procedure fail. +pub proc main + dropw + # => [pad(16)] + + # Call the faucet's burn procedure which handles all validations + call.faucets::burn + # => [pad(16)] +end diff --git a/crates/miden-standards/asm/standards/notes/mint.masm b/crates/miden-standards/asm/standards/notes/mint.masm new file mode 100644 index 0000000000..575bc04c3d --- /dev/null +++ b/crates/miden-standards/asm/standards/notes/mint.masm @@ -0,0 +1,128 @@ +use miden::protocol::active_note +use miden::protocol::note +use miden::standards::faucets::network_fungible->network_faucet + +# CONSTANTS +# ================================================================================================= + +const MINT_NOTE_NUM_INPUTS_PRIVATE=8 +const MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=12 + +const OUTPUT_NOTE_TYPE_PUBLIC=1 +const OUTPUT_NOTE_TYPE_PRIVATE=2 + +const OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=12 +const OUTPUT_PUBLIC_NOTE_INPUTS_LEN_MEM_ADDR=0 + +# ERRORS +# ================================================================================================= +const ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 8 inputs for private or 12+ inputs for public output notes" + +#! Network Faucet MINT script: mints assets by calling the network faucet's distribute function. +#! This note is intended to be executed against a network fungible faucet account. +#! +#! Requires that the account exposes: +#! - miden::standards::faucets::network_fungible::distribute procedure. +#! +#! Inputs: [ARGS, pad(12)] +#! Outputs: [pad(16)] +#! +#! Note inputs support two modes. Depending on the number of note inputs, +#! a private or public note is created on consumption of the MINT note: +#! +#! Private mode (8 inputs) - creates a private note: +#! - execution_hint: Execution hint for the output note +#! - aux: Auxiliary data for the output note +#! - tag: Note tag for the output note +#! - amount: The amount to mint +#! - RECIPIENT: The recipient digest (4 elements) +#! +#! Public mode (12+ inputs) - creates a public note with variable-length inputs: +#! - execution_hint: Execution hint for the output note +#! - aux: Auxiliary data for the output note +#! - tag: Note tag for the output note +#! - amount: The amount to mint +#! - SCRIPT_ROOT: Script root of the output note (4 elements) +#! - SERIAL_NUM: Serial number of the output note (4 elements) +#! - [INPUTS]: Variable-length inputs for the output note (Vec) +#! The number of output note inputs = num_mint_note_inputs - 12 +#! +#! Panics if: +#! - account does not expose distribute procedure. +#! - the number of inputs is not exactly 8 for private or less than 12 for public output notes. +pub proc main + dropw + # => [pad(16)] + # Load note inputs into memory starting at address 0 + push.0 exec.active_note::get_inputs + # => [total_inputs, inputs_ptr, pad(16)] + + dup + # => [num_inputs, num_inputs, inputs_ptr, pad(16)] + + u32assert2.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS + u32gte.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC + # => [is_public_output_note, total_inputs, inputs_ptr, pad(16)] + + if.true + # public output note creation + # => [total_inputs, inputs_ptr, pad(16)] + + movdn.9 drop + # => [EMPTY_WORD, EMPTY_WORD, total_inputs, pad(8)] + + mem_loadw_be.4 + # => [SCRIPT_ROOT, EMPTY_WORD, total_inputs, pad(8)] + + swapw mem_loadw_be.8 + # => [SERIAL_NUM, SCRIPT_ROOT, total_inputs, pad(8)] + + # compute variable length note inputs for the output note + movup.8 sub.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC + # => [num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] + + push.OUTPUT_PUBLIC_NOTE_INPUTS_ADDR + # => [inputs_ptr = 12, num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)] + + exec.note::build_recipient + # => [RECIPIENT, pad(12)] + + swapw mem_loadw_be.0 + # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + push.OUTPUT_NOTE_TYPE_PUBLIC + # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + movdn.3 + # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + + else + # private output note creation + + eq.MINT_NOTE_NUM_INPUTS_PRIVATE assert.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS drop + # => [inputs_ptr, pad(16)] + + drop + # => [pad(16)] + + mem_loadw_be.4 + # => [RECIPIENT, pad(12)] + + swapw mem_loadw_be.0 + # => [amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + push.OUTPUT_NOTE_TYPE_PRIVATE + # => [note_type, amount, tag, aux, execution_hint, RECIPIENT, pad(8)] + + movdn.3 + # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + + end + # => [amount, tag, aux, note_type, execution_hint, RECIPIENT, pad(8)] + + call.network_faucet::distribute + # => [pad(17))] + + drop + # => [pad(16)] +end diff --git a/crates/miden-standards/asm/standards/notes/p2id.masm b/crates/miden-standards/asm/standards/notes/p2id.masm new file mode 100644 index 0000000000..b315ad4e5a --- /dev/null +++ b/crates/miden-standards/asm/standards/notes/p2id.masm @@ -0,0 +1,53 @@ +use miden::protocol::active_account +use miden::protocol::account_id +use miden::protocol::active_note +use miden::standards::wallets::basic->basic_wallet + +# ERRORS +# ================================================================================================= + +const ERR_P2ID_WRONG_NUMBER_OF_INPUTS="P2ID note expects exactly 2 note inputs" + +const ERR_P2ID_TARGET_ACCT_MISMATCH="P2ID's target account address and transaction address do not match" + +#! 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. +#! +#! Requires that the account exposes: +#! - miden::standards::wallets::basic::receive_asset procedure. +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Note inputs are assumed to be as follows: +#! - target_account_id is the ID of the account for which the note is intended. +#! +#! Panics if: +#! - Account does not expose miden::standards::wallets::basic::receive_asset procedure. +#! - Account ID of executing account is not equal to the Account ID specified via note inputs. +#! - The same non-fungible asset already exists in the account. +#! - Adding a fungible asset would result in amount overflow, i.e., the total amount would be +#! greater than 2^63. +pub proc main + # store the note inputs to memory starting at address 0 + padw push.0 exec.active_note::get_inputs + # => [num_inputs, inputs_ptr, EMPTY_WORD] + + # make sure the number of inputs is 2 + eq.2 assert.err=ERR_P2ID_WRONG_NUMBER_OF_INPUTS + # => [inputs_ptr, EMPTY_WORD] + + # read the target account ID from the note inputs + mem_loadw_be drop drop + # => [target_account_id_prefix, target_account_id_suffix] + + exec.active_account::get_id + # => [account_id_prefix, account_id_suffix, target_account_id_prefix, target_account_id_suffix, ...] + + # ensure account_id = target_account_id, fails otherwise + exec.account_id::is_equal assert.err=ERR_P2ID_TARGET_ACCT_MISMATCH + # => [] + + exec.basic_wallet::add_assets_to_account + # => [] +end diff --git a/crates/miden-standards/asm/standards/notes/p2ide.masm b/crates/miden-standards/asm/standards/notes/p2ide.masm new file mode 100644 index 0000000000..1345bc4acb --- /dev/null +++ b/crates/miden-standards/asm/standards/notes/p2ide.masm @@ -0,0 +1,144 @@ +use miden::protocol::active_account +use miden::protocol::account_id +use miden::protocol::active_note +use miden::protocol::tx +use miden::standards::wallets::basic->basic_wallet + +# ERRORS +# ================================================================================================= + +const ERR_P2IDE_WRONG_NUMBER_OF_INPUTS="P2IDE note expects exactly 4 note inputs" + +const ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER="failed to reclaim P2IDE note because the reclaiming account is not the sender" + +const ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED="failed to reclaim P2IDE note because the reclaim block height is not reached yet" + +const ERR_P2IDE_RECLAIM_DISABLED="P2IDE reclaim is disabled" + +const ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED="failed to consume P2IDE note because the note is still timelocked" + +# HELPER PROCEDURES +# ================================================================================================= + +#! Helper procedure to check if the P2IDE note is unlocked. +#! +#! Inputs: [current_block_height, timelock_block_height] +#! Outputs: [current_block_height] +proc verify_unlocked + dup movdn.2 + # => [current_block_height, timelock_block_height, current_block_height] + + # check timelock_block_height <= current_block_height + lte assert.err=ERR_P2IDE_TIMELOCK_HEIGHT_NOT_REACHED + # => [current_block_height] +end + +#! Helper procedure which adds the note assets to the sender account. +#! +#! Checks if P2IDE reclaim is enabled and if true, if reclaim height has been reached. +#! +#! Inputs: [account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height] +#! Outputs: [] +#! +#! Panics if: +#! - the reclaim of the active note is disabled. +#! - the reclaim block height is not reached yet. +#! - the account attempting to reclaim the note is not the sender account. +proc reclaim_note + # check that the reclaim of the active note is enabled + movup.3 dup neq.0 assert.err=ERR_P2IDE_RECLAIM_DISABLED + # => [reclaim_block_height, account_id_prefix, account_id_suffix, current_block_height] + + # now check that sender is allowed to reclaim, reclaim block height <= current block height + movup.3 + # => [current_block_height, reclaim_block_height, account_id_prefix, account_id_suffix] + + lte assert.err=ERR_P2IDE_RECLAIM_HEIGHT_NOT_REACHED + # => [account_id_prefix, account_id_suffix] + + # if active account is not the target, we need to ensure it is the sender + exec.active_note::get_sender + # => [sender_account_id_prefix, sender_account_id_suffix, account_id_prefix, account_id_suffix] + + # ensure active account ID = sender account ID + exec.account_id::is_equal assert.err=ERR_P2IDE_RECLAIM_ACCT_IS_NOT_SENDER + # => [] + + # add note assets to account + exec.basic_wallet::add_assets_to_account + # => [] +end + +#! Extended Pay-to-ID note script (Reclaimable & Timelockable) +#! +#! Adds all assets from the note to the account if all of the following conditions are true: +#! - The transaction's reference block number is greater than or equal to the note's timelock block height. +#! - Any of the following conditions is true: +#! - The account ID against which the transaction is executed matches the note's target account id. +#! - The account ID against which the transaction is executed matches the note's sender account id and +#! the transaction's reference block number is greater than or equal to the note's reclaim block height. +#! +#! Requires that the account exposes: +#! - miden::standards::wallets::basic::receive_asset procedure. +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Note inputs are assumed to be as follows: +#! - target_account_id is the ID of the account for which the note is intended. +#! - reclaim_block_height is the block height at which the note can be reclaimed by the sender. +#! - timelock_block_height is the block height at which the note can be consumed by the target. +#! +#! Panics if: +#! - The account does not expose miden::standards::wallets::basic::receive_asset procedure. +#! - The note is consumed before the timelock expired, i.e. the transaction's reference block +#! number is less than the timelock block height. +#! - Before reclaim block height: the account ID of the executing account is not equal to the specified +#! account ID. +#! - At and after reclaim block height: the account ID of the executing account is not equal to +#! the specified account ID or sender account ID. +#! - The same non-fungible asset already exists in the account. +#! - Adding a fungible asset would result in an amount overflow, i.e., the total amount would be +#! greater than 2^63. +pub proc main + # store the note inputs to memory starting at address 0 + push.0 exec.active_note::get_inputs + # => [num_inputs, inputs_ptr] + + # make sure the number of inputs is 4 + eq.4 assert.err=ERR_P2IDE_WRONG_NUMBER_OF_INPUTS + # => [inputs_ptr] + + # read the reclaim block height, timelock_block_height, and target account ID from the note inputs + mem_loadw_be + # => [timelock_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] + + # read the current block number + exec.tx::get_block_number + # => [current_block_height, timelock_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] + + # fails if note is locked + exec.verify_unlocked + # => [current_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] + + # get active account id + exec.active_account::get_id dup.1 dup.1 + # => [account_id_prefix, account_id_suffix, account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height, target_account_id_prefix, target_account_id_suffix] + + # determine if the active account is the target account + movup.7 movup.7 exec.account_id::is_equal + # => [is_target, account_id_prefix, account_id_suffix, current_block_height, reclaim_block_height] + + if.true + # we can safely consume the note since the active account is the target of the note + dropw exec.basic_wallet::add_assets_to_account + # => [] + + else + # checks if active account is sender and if reclaim is enabled + exec.reclaim_note + # => [] + end + + # => [] +end diff --git a/crates/miden-standards/asm/standards/notes/swap.masm b/crates/miden-standards/asm/standards/notes/swap.masm new file mode 100644 index 0000000000..f95d718e92 --- /dev/null +++ b/crates/miden-standards/asm/standards/notes/swap.masm @@ -0,0 +1,112 @@ +use miden::protocol::active_note +use miden::protocol::output_note +use miden::standards::wallets::basic->wallet + +# CONSTANTS +# ================================================================================================= + +const SWAP_NOTE_INPUTS_NUMBER=12 + +# ERRORS +# ================================================================================================= + +const ERR_SWAP_WRONG_NUMBER_OF_INPUTS="SWAP script expects exactly 12 note inputs" + +const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset" + +#! Swap script: adds an asset from the note into consumers account and +#! creates a note consumable by note issuer containing requested ASSET. +#! +#! Requires that the account exposes: +#! - miden::standards::wallets::basic::receive_asset procedure. +#! - miden::standards::wallets::basic::move_asset_to_note procedure. +#! +#! Inputs: [ARGS] +#! Outputs: [] +#! +#! Note inputs are assumed to be as follows: +#! - REQUESTED_ASSET +#! - PAYBACK_NOTE_RECIPIENT +#! - payback_note_execution_hint +#! - payback_note_type +#! - payback_note_aux +#! - payback_note_tag +#! +#! Panics if: +#! - account does not expose miden::standards::wallets::basic::receive_asset procedure. +#! - account does not expose miden::standards::wallets::basic::move_asset_to_note procedure. +#! - account vault does not contain the requested asset. +#! - adding a fungible asset would result in amount overflow, i.e., the total amount would be +#! greater than 2^63. +pub proc main + # dropping note args + dropw + # => [] + + # --- create a payback note with the requested asset ---------------- + + # store note inputs into memory starting at address 0 + push.0 exec.active_note::get_inputs + # => [num_inputs, inputs_ptr] + + # make sure the number of inputs is 12 + eq.SWAP_NOTE_INPUTS_NUMBER assert.err=ERR_SWAP_WRONG_NUMBER_OF_INPUTS + # => [inputs_ptr] + + # load REQUESTED_ASSET + mem_loadw_be + # => [REQUESTED_ASSET] + + # load PAYBACK_NOTE_RECIPIENT + padw mem_loadw_be.4 + # => [PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET] + + # load payback P2ID details + padw mem_loadw_be.8 + # => [tag, aux, note_type, execution_hint, PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET] + + # create payback P2ID note + exec.output_note::create + # => [note_idx, REQUESTED_ASSET] + + movdn.4 + # => [REQUESTED_ASSET, note_idx] + + # padding stack with 11 zeros + repeat.11 + push.0 + movdn.5 + end + # => [REQUESTED_ASSET, note_idx, pad(11)] + + # move asset to the note + call.wallet::move_asset_to_note + # => [REQUESTED_ASSET, note_idx, pad(11)] + + dropw drop push.0 + # => [pad(12)] + + # --- move assets from the SWAP note into the account ------------------------- + + # store the number of note assets to memory starting at address 0 + push.0 exec.active_note::get_assets + # => [num_assets, ptr, pad(12)] + + # make sure the number of assets is 1 + assert.err=ERR_SWAP_WRONG_NUMBER_OF_ASSETS + # => [ptr, pad(12)] + + # load the ASSET + mem_loadw_be + # => [ASSET, pad(12)] + + # add the ASSET to the account + call.wallet::receive_asset + # => [pad(16)] + + # clearing the stack of padded 0s + repeat.4 + dropw + end + # => [] +end diff --git a/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs index a187b57729..e26661972c 100644 --- a/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs +++ b/crates/miden-standards/src/account/auth/ecdsa_k256_keccak.rs @@ -12,7 +12,7 @@ static ECDSA_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// An [`AccountComponent`] implementing the ECDSA K256 Keccak signature scheme for authentication /// of transactions. /// -/// It reexports the procedures from `miden::contracts::auth::ecdsa_k256_keccak`. When linking +/// It reexports the procedures from `miden::standards::auth::ecdsa_k256_keccak`. When linking /// against this component, the `miden` library (i.e. /// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the /// case when using [`CodeBuilder`][builder]. The procedures of this component are: diff --git a/crates/miden-standards/src/account/auth/rpo_falcon_512.rs b/crates/miden-standards/src/account/auth/rpo_falcon_512.rs index 305f5c7c46..a65d9e0394 100644 --- a/crates/miden-standards/src/account/auth/rpo_falcon_512.rs +++ b/crates/miden-standards/src/account/auth/rpo_falcon_512.rs @@ -12,7 +12,7 @@ static FALCON_PUBKEY_SLOT_NAME: LazyLock = LazyLock::new(|| { /// An [`AccountComponent`] implementing the RpoFalcon512 signature scheme for authentication of /// transactions. /// -/// It reexports the procedures from `miden::contracts::auth::rpo_falcon512`. When linking against +/// It reexports the procedures from `miden::standards::auth::rpo_falcon512`. When linking against /// this component, the `miden` library (i.e. [`ProtocolLib`](miden_protocol::ProtocolLib)) must /// be available to the assembler which is the case when using [`CodeBuilder`][builder]. The /// procedures of this component are: diff --git a/crates/miden-standards/src/account/faucets/basic_fungible.rs b/crates/miden-standards/src/account/faucets/basic_fungible.rs index 329937ebd4..57c29385e8 100644 --- a/crates/miden-standards/src/account/faucets/basic_fungible.rs +++ b/crates/miden-standards/src/account/faucets/basic_fungible.rs @@ -42,7 +42,7 @@ procedure_digest!( /// An [`AccountComponent`] implementing a basic fungible faucet. /// -/// It reexports the procedures from `miden::contracts::faucets::basic_fungible`. When linking +/// It reexports the procedures from `miden::standards::faucets::basic_fungible`. When linking /// against this component, the `miden` library (i.e. /// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the /// case when using [`CodeBuilder`][builder]. The procedures of this component are: diff --git a/crates/miden-standards/src/account/faucets/network_fungible.rs b/crates/miden-standards/src/account/faucets/network_fungible.rs index 912e89e863..502e8582df 100644 --- a/crates/miden-standards/src/account/faucets/network_fungible.rs +++ b/crates/miden-standards/src/account/faucets/network_fungible.rs @@ -43,7 +43,7 @@ static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { /// An [`AccountComponent`] implementing a network fungible faucet. /// -/// It reexports the procedures from `miden::contracts::faucets::network_fungible`. When linking +/// It reexports the procedures from `miden::standards::faucets::network_fungible`. When linking /// against this component, the `miden` library (i.e. /// [`ProtocolLib`](miden_protocol::ProtocolLib)) must be available to the assembler which is the /// case when using [`CodeBuilder`][builder]. The procedures of this component are: diff --git a/crates/miden-standards/src/account/wallets/mod.rs b/crates/miden-standards/src/account/wallets/mod.rs index fc7c4f0773..fe412229a7 100644 --- a/crates/miden-standards/src/account/wallets/mod.rs +++ b/crates/miden-standards/src/account/wallets/mod.rs @@ -41,7 +41,7 @@ procedure_digest!( /// An [`AccountComponent`] implementing a basic wallet. /// -/// It reexports the procedures from `miden::contracts::wallets::basic`. When linking against this +/// It reexports the procedures from `miden::standards::wallets::basic`. When linking against this /// component, the `miden` library (i.e. [`ProtocolLib`](miden_protocol::ProtocolLib)) must be /// available to the assembler which is the case when using [`CodeBuilder`][builder]. The procedures /// of this component are: diff --git a/docs/src/account/code.md b/docs/src/account/code.md index 2b22c67b06..ed6d60ab1e 100644 --- a/docs/src/account/code.md +++ b/docs/src/account/code.md @@ -17,7 +17,7 @@ Every Miden `Account` is essentially a smart contract. The `Code` defines the ac ## Interface -An account's code is typically the result of merging multiple [account components](./components). This results in a set of procedures that make up the _interface_ of the account. As an example, a typical wallet uses the so-called _basic wallet_ interface, which is defined in `miden::contracts::wallets::basic`. It consists of the `receive_asset` and `move_asset_to_note` procedures. If an account has this interface, i.e. this set of procedures, it can consume standard [P2ID notes](../note#p2id-pay-to-id). If it doesn't, it can't consume this type of note. So, adhering to standard interfaces such as the basic wallet will generally make an account more interoperable. +An account's code is typically the result of merging multiple [account components](./components). This results in a set of procedures that make up the _interface_ of the account. As an example, a typical wallet uses the so-called _basic wallet_ interface, which is defined in `miden::standards::wallets::basic`. It consists of the `receive_asset` and `move_asset_to_note` procedures. If an account has this interface, i.e. this set of procedures, it can consume standard [P2ID notes](../note#p2id-pay-to-id). If it doesn't, it can't consume this type of note. So, adhering to standard interfaces such as the basic wallet will generally make an account more interoperable. ## Authentication diff --git a/docs/src/note.md b/docs/src/note.md index 2137a7db1b..20f30201f2 100644 --- a/docs/src/note.md +++ b/docs/src/note.md @@ -121,7 +121,7 @@ Only those who know the RECIPIENT’s pre-image can consume the `Note`. For priv The [transaction prologue](transaction) requires all necessary data to compute the `Note` hash. This setup allows scenario-specific restrictions on who may consume a `Note`. -For a practical example, refer to the [SWAP note script](https://github.com/0xMiden/miden-base/blob/next/crates/miden-standards/asm/note_scripts/SWAP.masm), where the RECIPIENT ensures that only a defined target can consume the swapped asset. +For a practical example, refer to the [SWAP note script](https://github.com/0xMiden/miden-base/blob/next/crates/miden-standards/asm/standards/notes/swap.masm), where the RECIPIENT ensures that only a defined target can consume the swapped asset. #### Note nullifier ensuring private consumption @@ -156,7 +156,7 @@ The P2ID note script implements a simple pay-to-account-ID pattern. It adds all - **Purpose:** Direct asset transfer to a specific account ID - **Inputs:** Requires exactly 2 note inputs containing the target account ID - **Validation:** Ensures the consuming account's ID matches the target account ID specified in the note -- **Requirements:** Target account must expose the `miden::contracts::wallets::basic::receive_asset` procedure +- **Requirements:** Target account must expose the `miden::standards::wallets::basic::receive_asset` procedure **Use case:** Simple, direct payments where you want to send assets to a known account ID. @@ -174,7 +174,7 @@ The P2IDE note script extends P2ID with additional features including time-locki - **Time-lock:** Note cannot be consumed until the specified block height is reached - **Reclaim:** Original sender can reclaim the note after the reclaim block height if not consumed by target - **Validation:** Complex logic to handle both target consumption and sender reclaim scenarios -- **Requirements:** Account must expose the `miden::contracts::wallets::basic::receive_asset` procedure +- **Requirements:** Account must expose the `miden::standards::wallets::basic::receive_asset` procedure **Use cases:** @@ -198,8 +198,8 @@ The SWAP note script implements atomic asset swapping functionality. 1. Creates a payback note containing the requested asset for the original note issuer 2. Adds the note's asset to the consuming account's vault - **Requirements:** Account must expose both: - - `miden::contracts::wallets::basic::receive_asset` procedure - - `miden::contracts::wallets::basic::move_asset_to_note` procedure + - `miden::standards::wallets::basic::receive_asset` procedure + - `miden::standards::wallets::basic::move_asset_to_note` procedure **Use case:** Decentralized asset trading where two parties want to exchange different assets atomically.