diff --git a/CHANGELOG.md b/CHANGELOG.md index 910ef417d..ad5ed1cc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - Made `AccountIdError` public (#1067). - Made `BasicFungibleFaucet::MAX_DECIMALS` public (#1063). - [BREAKING] Removed `miden-tx-prover` crate and created `miden-proving-service` and `miden-remote-provers` (#1047). +- Deduplicate `masm` procedures across kernel and miden lib to a shared `util` module (#1070). ## 0.6.2 (2024-11-20) diff --git a/miden-lib/asm/kernels/transaction/lib/account.masm b/miden-lib/asm/kernels/transaction/lib/account.masm index e747335ec..269ab9648 100644 --- a/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/miden-lib/asm/kernels/transaction/lib/account.masm @@ -3,6 +3,7 @@ use.std::collections::smt use.std::crypto::hashes::rpo use.std::mem +use.kernel::util::account_id use.kernel::constants use.kernel::memory @@ -12,9 +13,6 @@ use.kernel::memory # Account nonce cannot be increased by a greater than u32 value const.ERR_ACCOUNT_NONCE_INCREASE_MUST_BE_U32=0x00020004 -# Least significant byte of the account ID suffix must be zero. -const.ERR_ACCOUNT_ID_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO=0x00020005 - # Account code must be updatable for it to be possible to set new code const.ERR_ACCOUNT_CODE_IS_NOT_UPDATABLE=0x00020006 @@ -81,6 +79,20 @@ const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE=0x00020059 # CONSTANTS # ================================================================================================= +# The account storage slot at which faucet data is stored. +# Fungible faucet: The faucet data consists of [0, 0, 0, total_issuance] +# Non-fungible faucet: The faucet data consists of SMT root containing minted non-fungible assets. +const.FAUCET_STORAGE_DATA_SLOT=0 + +# The maximum storage slot index +const.MAX_STORAGE_SLOT_INDEX=254 + +# The maximum number of account storage slots. +const.MAX_NUM_STORAGE_SLOTS=MAX_STORAGE_SLOT_INDEX+1 + +# The maximum number of account interface procedures. +const.MAX_NUM_PROCEDURES=256 + # Given the least significant 32 bits of an account ID's prefix, this mask defines the bits used # to determine the account version. const.ACCOUNT_VERSION_MASK_U32=0x0f # 0b1111 @@ -117,20 +129,6 @@ const.NON_FUNGIBLE_FAUCET_ACCOUNT=0x30 # 0b11_0000 # Bit pattern for a faucet account, after the account type mask has been applied. const.FAUCET_ACCOUNT=0x20 # 0b10_0000 -# The maximum number of account interface procedures. -const.MAX_NUM_PROCEDURES=256 - -# The account storage slot at which faucet data is stored. -# Fungible faucet: The faucet data consists of [0, 0, 0, total_issuance] -# Non-fungible faucet: The faucet data consists of SMT root containing minted non-fungible assets. -const.FAUCET_STORAGE_DATA_SLOT=0 - -# The maximum storage slot index -const.MAX_STORAGE_SLOT_INDEX=254 - -# The maximum number of account storage slots. -const.MAX_NUM_STORAGE_SLOTS=MAX_STORAGE_SLOT_INDEX+1 - # Depth of the account database tree. const.ACCOUNT_TREE_DEPTH=64 @@ -282,10 +280,7 @@ export.memory::get_init_acct_hash->get_initial_hash #! Where: #! - acct_id_prefix is the prefix of the account ID. #! - is_fungible_faucet is a boolean indicating whether the account is a fungible faucet. -export.is_fungible_faucet - exec.type push.FUNGIBLE_FAUCET_ACCOUNT eq - # => [is_fungible_faucet] -end +export.::kernel::util::account_id::is_fungible_faucet #! Returns a boolean indicating whether the account is a non-fungible faucet. #! @@ -295,10 +290,7 @@ end #! Where: #! - acct_id_prefix is the prefix of the account ID. #! - is_non_fungible_faucet is a boolean indicating whether the account is a non-fungible faucet. -export.is_non_fungible_faucet - exec.type push.NON_FUNGIBLE_FAUCET_ACCOUNT eq - # => [is_non_fungible_faucet] -end +export.::kernel::util::account_id::is_non_fungible_faucet #! Returns a boolean indicating whether the account is a faucet. #! @@ -308,10 +300,7 @@ end #! Where: #! - acct_id_prefix is the prefix of the account ID. #! - is_faucet is a boolean indicating whether the account is a faucet. -export.is_faucet - u32split drop push.FAUCET_ACCOUNT u32and eq.0 not - # => [is_faucet] -end +export.::kernel::util::account_id::is_faucet #! Returns a boolean indicating whether the account is a regular updatable account. #! @@ -322,10 +311,7 @@ end #! - acct_id_prefix is the prefix of the account ID. #! - is_updatable_account is a boolean indicating whether the account is a regular updatable #! account. -export.is_updatable_account - exec.type push.REGULAR_ACCOUNT_UPDATABLE_CODE eq - # => [is_updatable_account] -end +export.::kernel::util::account_id::is_updatable_account #! Returns a boolean indicating whether the account is a regular immutable account. #! @@ -336,10 +322,7 @@ end #! - acct_id_prefix is the prefix of the account ID. #! - is_immutable_account is a boolean indicating whether the account is a regular immutable #! account. -export.is_immutable_account - exec.type push.REGULAR_ACCOUNT_IMMUTABLE_CODE eq - # => [is_immutable_account] -end +export.::kernel::util::account_id::is_immutable_account #! Returns a boolean indicating whether the given account_ids are equal. #! @@ -350,14 +333,7 @@ end #! - acct_id_{prefix,suffix} are the prefix and suffix felts of an account ID. #! - other_acct_id_{prefix,suffix} are the prefix and suffix felts of the other account ID to compare against. #! - is_id_equal is a boolean indicating whether the account IDs are equal. -export.is_id_eq - movup.2 eq - # => [is_prefix_equal, acct_id_suffix, other_acct_id_suffix] - movdn.2 eq - # => [is_suffix_equal, is_prefix_equal] - and - # => [is_id_equal] -end +export.::kernel::util::account_id::is_equal->is_id_equal #! Validates an account ID. Note that this does not validate anything about the account type, #! since any bit pattern is a valid account type. @@ -373,43 +349,7 @@ end #! - account_id_prefix does not contain either the public or private storage mode. #! - account_id_suffix contains an anchor epoch that is greater or equal to 2^16. #! - account_id_suffix does not have its lower 8 bits set to zero. -export.validate_id - # Validate version in prefix. For now only version 0 is supported. - # --------------------------------------------------------------------------------------------- - - dup exec.id_version - # => [id_version, account_id_prefix, account_id_suffix] - assertz.err=ERR_ACCOUNT_ID_UNKNOWN_VERSION - # => [account_id_prefix, account_id_suffix] - - # Validate storage mode in prefix. - # --------------------------------------------------------------------------------------------- - - u32split drop - # => [account_id_prefix_lo, account_id_suffix] - u32and.ACCOUNT_ID_STORAGE_MODE_MASK_U32 dup eq.ACCOUNT_ID_STORAGE_MODE_PRIVATE_U32 - # => [is_private_storage_mode, id_storage_mode_masked, account_id_suffix] - swap eq.ACCOUNT_ID_STORAGE_MODE_PUBLIC_U32 - # => [is_public_storage_mode, is_private_storage_mode, account_id_suffix] - or assert.err=ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE - # => [account_id_suffix] - - # Validate anchor epoch is less than u16::MAX (0xffff) in suffix. - # --------------------------------------------------------------------------------------------- - - dup exec.id_anchor_epoch - # => [anchor_epoch, account_id_suffix] - lt.0xffff assert.err=ERR_ACCOUNT_ID_EPOCH_MUST_BE_LESS_THAN_U16_MAX - # => [account_id_suffix] - - # Validate lower 8 bits of suffix are zero. - # --------------------------------------------------------------------------------------------- - - u32split drop u32and.0xff eq.0 - # => [is_least_significant_byte_zero] - assert.err=ERR_ACCOUNT_ID_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO - # => [] -end +export.::kernel::util::account_id::validate->validate_id #! Sets the code of the account the transaction is being executed against. #! @@ -783,7 +723,7 @@ export.validate_seed # => [0, 0, account_id_prefix, account_id_suffix] # get the anchor block's number - dup.3 exec.id_anchor_block_num + dup.3 exec.account_id::id_anchor_block_num # => [anchor_block_num, 0, 0, account_id_prefix, account_id_suffix] exec.memory::get_chain_mmr_ptr swap @@ -847,112 +787,18 @@ export.validate_seed # => [account_id_suffix, hashed_account_id_prefix, hashed_account_id_suffix, account_id_prefix] # extract anchor epoch from ID of the new account - dup movdn.4 exec.id_anchor_epoch + dup movdn.4 exec.account_id::id_anchor_epoch # => [anchor_epoch, hashed_account_id_prefix, hashed_account_id_suffix, account_id_prefix, account_id_suffix] # shape suffix of hashed id, adding the anchor epoch and setting the lower 8 bits to zero - movup.2 exec.shape_suffix swap + movup.2 exec.account_id::shape_suffix swap # => [hashed_account_id_prefix, hashed_account_id_suffix, account_id_prefix, account_id_suffix] # assert the account ID matches the account ID of the new account - exec.is_id_eq assert.err=ERR_ACCOUNT_SEED_ANCHOR_BLOCK_HASH_DIGEST_MISMATCH + exec.is_id_equal assert.err=ERR_ACCOUNT_SEED_ANCHOR_BLOCK_HASH_DIGEST_MISMATCH # => [] end -#! Shapes the suffix so it meets the requirements of the account ID, by overwriting the -#! upper 16 bits with the anchor epoch and setting the lower 8 bits to zero. -#! -#! Inputs: [seed_digest_suffix, anchor_epoch] -#! Outputs: [account_id_suffix] -#! -#! Where: -#! - seed_digest_suffix is the suffix of the digest that should be shaped into the suffix -#! of an account ID. -#! - account_id_suffix is the suffix of an account ID. -#! - anchor_epoch is the epoch number to which this account ID is anchored. -proc.shape_suffix - u32split - # => [seed_digest_suffix_hi, seed_digest_suffix_lo, anchor_epoch] - - # clear epoch bits in hi part so we can set them later - u32and.0x0000ffff swap - # => [seed_digest_suffix_lo, seed_digest_suffix_hi', anchor_epoch] - - # clear lower 8 bits of the lo part - u32and.0xffffff00 swap.2 - # => [anchor_epoch, seed_digest_suffix_hi', seed_digest_suffix_lo'] - - # assert epoch is not 2^16 - # this is technically optional as we will compare this id with the provided one for which - # this property was already checked, but since this check is cheap we include it anyway - dup eq.0xffff assertz.err=ERR_ACCOUNT_ID_EPOCH_MUST_BE_LESS_THAN_U16_MAX - # => [anchor_epoch, seed_digest_suffix_hi', seed_digest_suffix_lo'] - - # shift epoch left by 16 bits and set epoch bits on hi part - u32shl.16 u32or - # => [seed_digest_suffix_hi'', seed_digest_suffix_lo'] - - # reassemble the suffix by multiplying the hi part with 2^32 and adding the lo part - mul.0x0100000000 add - # => [account_id_suffix] -end - -#! Extracts the block number of the anchor block from the suffix of an account ID. -#! -#! Inputs: [account_id_suffix] -#! Outputs: [anchor_block_num] -#! -#! Where: -#! - account_id_suffix is the suffix of an account ID. -#! - anchor_block_num is the number of the block to which this account ID is anchored. -proc.id_anchor_block_num - # extract the upper 32 bits - u32split swap drop - # => [account_id_suffix_hi] - - # to get the epoch's block number we would have to multiply the epoch in the account ID by 2^16 - # since the epoch is already in the upper 16 bits of the u32, we can simply zero out the - # lower 16 bits to achieve the same result. - u32and.0xffff0000 - # => [anchor_block_num] -end - -#! Extracts the epoch from the suffix of an account ID. -#! -#! Inputs: [account_id_suffix] -#! Outputs: [anchor_epoch] -#! -#! Where: -#! - account_id_suffix is the suffix of an account ID. -#! - anchor_epoch is the epoch number to which this account ID is anchored. -proc.id_anchor_epoch - # extract the upper 32 bits - u32split swap drop - # => [account_id_suffix_hi] - - # shift the upper 16 bits to the right to produce the epoch - u32shr.16 - # => [anchor_epoch] -end - -#! Extracts the account ID version from the prefix of an account ID. -#! -#! Inputs: [account_id_prefix] -#! Outputs: [id_version] -#! -#! Where: -#! - account_id_prefix is the prefix of an account ID. -#! - id_version is the version number of the ID. -proc.id_version - # extract the lower 32 bits - u32split drop - # => [account_id_prefix_lo] - - # mask out the version - u32and.ACCOUNT_VERSION_MASK_U32 - # => [id_version] -end - # DATA LOADERS # ================================================================================================= @@ -1097,25 +943,6 @@ end # HELPER PROCEDURES # ================================================================================================= -#! Returns the least significant half of an account ID prefix with the account type bits masked out. -#! -#! The account type can be obtained by comparing this value with the following constants: -#! - REGULAR_ACCOUNT_UPDATABLE_CODE -#! - REGULAR_ACCOUNT_IMMUTABLE_CODE -#! - FUNGIBLE_FAUCET_ACCOUNT -#! - NON_FUNGIBLE_FAUCET_ACCOUNT -#! -#! Inputs: [acct_id_prefix] -#! Outputs: [acct_type] -#! -#! Where: -#! - acct_id_prefix is the prefix of the account ID. -#! - acct_type is the account type. -proc.type - u32split drop push.ACCOUNT_ID_TYPE_MASK_U32 u32and - # => [acct_type] -end - #! Sets an item in the account storage. Doesn't emit any events. #! #! Inputs: [index, NEW_VALUE] @@ -1191,7 +1018,7 @@ export.get_foreign_account_ptr # => [foreign_account_id_prefix, foreign_account_id_suffix] # check that foreign account ID is not equal to the native account ID - dup.1 dup.1 exec.memory::get_native_account_id exec.is_id_eq not + dup.1 dup.1 exec.memory::get_native_account_id exec.is_id_equal not assert.err=ERR_FOREIGN_ACCOUNT_ID_EQUALS_NATIVE_ACCT_ID # get the initial account data pointer @@ -1221,7 +1048,7 @@ export.get_foreign_account_ptr # => [is_empty_block, maybe_account_id_prefix, maybe_account_id_suffix, curr_account_ptr', foreign_account_id_prefix, foreign_account_id_suffix] # check whether the current id matches the foreign id - movdn.2 dup.5 dup.5 exec.is_id_eq + movdn.2 dup.5 dup.5 exec.is_id_equal # => [is_equal_id, is_empty_word, curr_account_ptr', foreign_account_id_prefix, foreign_account_id_suffix] # get the loop flag @@ -1233,7 +1060,7 @@ export.get_foreign_account_ptr # check that the loading of one more account won't exceed the maximum number of the foreign # accounts which can be loaded. - dup exec.memory::get_max_foreign_account_ptr lte + dup exec.memory::get_max_foreign_account_ptr lte assert.err=ERR_FOREIGN_ACCOUNT_MAX_NUMBER_EXCEEDED # => [curr_account_ptr, foreign_account_id_prefix, foreign_account_id_suffix, is_equal_id] diff --git a/miden-lib/asm/kernels/transaction/lib/asset.masm b/miden-lib/asm/kernels/transaction/lib/asset.masm index d38b257fd..1bd6f7bf3 100644 --- a/miden-lib/asm/kernels/transaction/lib/asset.masm +++ b/miden-lib/asm/kernels/transaction/lib/asset.masm @@ -24,11 +24,9 @@ const.ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN=0x00020026 # The origin of the non-fungible asset is not this faucet const.ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN=0x00020027 -# CONSTANTS +# CONSTANT ACCESSORS # ================================================================================================= -const.FUNGIBLE_ASSET_MAX_AMOUNT=9223372036854775807 - #! Returns the maximum amount of a fungible asset. #! #! Inputs: [] @@ -36,10 +34,7 @@ const.FUNGIBLE_ASSET_MAX_AMOUNT=9223372036854775807 #! #! Where: #! - fungible_asset_max_amount is the maximum amount of a fungible asset. -export.get_fungible_asset_max_amount - push.FUNGIBLE_ASSET_MAX_AMOUNT - # => [fungible_asset_max_amount] -end +export.::kernel::util::asset::get_fungible_asset_max_amount # PROCEDURES # ================================================================================================= @@ -69,7 +64,7 @@ export.validate_fungible_asset # => [ASSET] # assert that the max amount (ASSET[0]) of a fungible asset is not exceeded - dup.3 push.FUNGIBLE_ASSET_MAX_AMOUNT lte + dup.3 exec.get_fungible_asset_max_amount lte assert.err=ERR_FUNGIBLE_ASSET_FORMAT_ELEMENT_ZERO_MUST_BE_WITHIN_LIMITS # => [ASSET] end @@ -166,7 +161,7 @@ export.validate_fungible_asset_origin dup.3 dup.3 # => [asset_id_prefix, asset_id_suffix, faucet_id_prefix, faucet_id_suffix, ASSET] - exec.account::is_id_eq assert.err=ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN + exec.account::is_id_equal assert.err=ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN # => [ASSET] # assert the fungible asset is valid diff --git a/miden-lib/asm/kernels/transaction/lib/constants.masm b/miden-lib/asm/kernels/transaction/lib/constants.masm index 6ee55fb5d..fb48f9455 100644 --- a/miden-lib/asm/kernels/transaction/lib/constants.masm +++ b/miden-lib/asm/kernels/transaction/lib/constants.masm @@ -51,9 +51,7 @@ end #! #! Where: #! - max_inputs_per_note is the max inputs per note. -export.get_max_inputs_per_note - push.MAX_INPUTS_PER_NOTE -end +export.::kernel::util::note::get_max_inputs_per_note #! Returns the max allowed number of assets per note. #! diff --git a/miden-lib/asm/kernels/transaction/lib/prologue.masm b/miden-lib/asm/kernels/transaction/lib/prologue.masm index ce35a82f2..83c2e3059 100644 --- a/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -469,7 +469,7 @@ proc.process_account_data # assert the account ID matches the account ID in global inputs exec.memory::get_global_acct_id exec.memory::get_account_id - exec.account::is_id_eq assert.err=ERR_PROLOGUE_MISMATCH_OF_ACCOUNT_IDS_FROM_GLOBAL_INPUTS_AND_ADVICE_PROVIDER + exec.account::is_id_equal assert.err=ERR_PROLOGUE_MISMATCH_OF_ACCOUNT_IDS_FROM_GLOBAL_INPUTS_AND_ADVICE_PROVIDER # => [ACCT_HASH] # store a copy of the initial nonce in global inputs diff --git a/miden-lib/asm/miden/account.masm b/miden-lib/asm/miden/account.masm index bc48b7da9..b882d6d6b 100644 --- a/miden-lib/asm/miden/account.masm +++ b/miden-lib/asm/miden/account.masm @@ -439,39 +439,6 @@ export.get_vault_commitment # => [COM] end -# PROCEDURES COPIED FROM KERNEL (TODO: get rid of this duplication) -# ================================================================================================= - -# Given the least significant 32 bits of an account ID's prefix, this mask defines the bits used -# to determine the account type. -const.ACCOUNT_ID_TYPE_MASK_U32=0x30 # 0b11_0000 - -# Bit pattern for a fungible faucet w/ immutable code, after the account type mask has been applied. -const.FUNGIBLE_FAUCET_ACCOUNT=0x20 # 0b10_0000 - -# Bit pattern for a non-fungible faucet w/ immutable code, after the account type mask has been -# applied. -const.NON_FUNGIBLE_FAUCET_ACCOUNT=0x30 # 0b11_0000 - -#! Returns the most significant half with the account type bits masked out. -#! -#! The account type can be defined by comparing this value with the following constants: -#! -#! - REGULAR_ACCOUNT_UPDATABLE_CODE -#! - REGULAR_ACCOUNT_IMMUTABLE_CODE -#! - FUNGIBLE_FAUCET_ACCOUNT -#! - NON_FUNGIBLE_FAUCET_ACCOUNT -#! -#! Stack: [acct_id_prefix] -#! Output: [acct_type] -#! -#! - acct_id_prefix is the prefix of the account ID. -#! - acct_type is the account type. -proc.type - u32split drop push.ACCOUNT_ID_TYPE_MASK_U32 u32and - # => [acct_type] -end - #! Returns a boolean indicating whether the account is a fungible faucet. #! #! Stack: [acct_id] @@ -479,10 +446,7 @@ end #! #! - acct_id is the account ID. #! - is_fungible_faucet is a boolean indicating whether the account is a fungible faucet. -export.is_fungible_faucet - exec.type push.FUNGIBLE_FAUCET_ACCOUNT eq - # => [is_fungible_faucet] -end +export.::miden::util::account_id::is_fungible_faucet #! Returns a boolean indicating whether the account is a non-fungible faucet. #! @@ -491,13 +455,8 @@ end #! #! - acct_id is the account ID. #! - is_non_fungible_faucet is a boolean indicating whether the account is a non-fungible faucet. -export.is_non_fungible_faucet - exec.type push.NON_FUNGIBLE_FAUCET_ACCOUNT eq - # => [is_non_fungible_faucet] -end +export.::miden::util::account_id::is_non_fungible_faucet -#! TODO: This is a copy; move to utils. -#! #! Returns a boolean indicating whether the given account_ids are equal. #! #! Inputs: [acct_id_prefix, acct_id_suffix, other_acct_id_prefix, other_acct_id_suffix] @@ -507,11 +466,4 @@ end #! - acct_id_{prefix,suffix} are the prefix and suffix felts of an account ID. #! - other_acct_id_{prefix,suffix} are the prefix and suffix felts of the other account ID to compare against. #! - is_id_equal is a boolean indicating whether the account IDs are equal. -export.is_id_eq - movup.2 eq - # => [is_prefix_equal, acct_id_suffix, other_acct_id_suffix] - movdn.2 eq - # => [is_suffix_equal, is_prefix_equal] - and - # => [is_id_equal] -end +export.::miden::util::account_id::is_equal->is_id_equal diff --git a/miden-lib/asm/miden/asset.masm b/miden-lib/asm/miden/asset.masm index d1b9d2dc2..3f90899f1 100644 --- a/miden-lib/asm/miden/asset.masm +++ b/miden-lib/asm/miden/asset.masm @@ -104,18 +104,10 @@ export.create_non_fungible_asset # => [ASSET] end -# PROCEDURES COPIED FROM KERNEL (TODO: get rid of this duplication) -# ================================================================================================= - -const.FUNGIBLE_ASSET_MAX_AMOUNT=9223372036854775807 - #! Returns the maximum amount of a fungible asset. #! #! Stack: [] #! Outputs: [fungible_asset_max_amount] #! #! fungible_asset_max_amount is the maximum amount of a fungible asset. -export.get_fungible_asset_max_amount - push.FUNGIBLE_ASSET_MAX_AMOUNT - # => [fungible_asset_max_amount] -end \ No newline at end of file +export.::miden::util::asset::get_fungible_asset_max_amount diff --git a/miden-lib/asm/miden/note.masm b/miden-lib/asm/miden/note.masm index 070854571..0e663f9ff 100644 --- a/miden-lib/asm/miden/note.masm +++ b/miden-lib/asm/miden/note.masm @@ -265,6 +265,4 @@ const.MAX_INPUTS_PER_NOTE=128 #! Output: [max_inputs_per_note] #! #! - max_inputs_per_note is the max inputs per note. -export.get_max_inputs_per_note - push.MAX_INPUTS_PER_NOTE -end +export.::miden::util::note::get_max_inputs_per_note diff --git a/miden-lib/asm/note_scripts/P2ID.masm b/miden-lib/asm/note_scripts/P2ID.masm index aed3a5cb9..21168c2ff 100644 --- a/miden-lib/asm/note_scripts/P2ID.masm +++ b/miden-lib/asm/note_scripts/P2ID.masm @@ -98,7 +98,7 @@ begin # => [account_id_prefix, account_id_suffix, target_account_id_prefix, target_account_id_suffix, ...] # ensure account_id = target_account_id, fails otherwise - exec.account::is_id_eq assert.err=ERR_P2ID_TARGET_ACCT_MISMATCH + exec.account::is_id_equal assert.err=ERR_P2ID_TARGET_ACCT_MISMATCH # => [] exec.add_note_assets_to_account diff --git a/miden-lib/asm/note_scripts/P2IDR.masm b/miden-lib/asm/note_scripts/P2IDR.masm index c0ff7a73e..cfa456abe 100644 --- a/miden-lib/asm/note_scripts/P2IDR.masm +++ b/miden-lib/asm/note_scripts/P2IDR.masm @@ -107,7 +107,7 @@ begin # => [account_id_prefix, account_id_suffix, account_id_prefix, account_id_suffix, reclaim_block_height, target_account_id_prefix, target_account_id_suffix, ...] # determine if the current account is the target account - movup.6 movup.6 exec.account::is_id_eq + movup.6 movup.6 exec.account::is_id_equal # => [is_target, account_id_prefix, account_id_suffix, reclaim_block_height] if.true @@ -121,7 +121,7 @@ begin # => [sender_account_id_prefix, sender_account_id_suffix, account_id_prefix, account_id_suffix, reclaim_block_height] # ensure current account ID = sender account ID - exec.account::is_id_eq assert.err=ERR_P2IDR_RECLAIM_ACCT_IS_NOT_SENDER + exec.account::is_id_equal assert.err=ERR_P2IDR_RECLAIM_ACCT_IS_NOT_SENDER # => [reclaim_block_height] # now check that sender is allowed to reclaim, current block >= reclaim block height diff --git a/miden-lib/asm/shared/util/account_id.masm b/miden-lib/asm/shared/util/account_id.masm new file mode 100644 index 000000000..7c3785a59 --- /dev/null +++ b/miden-lib/asm/shared/util/account_id.masm @@ -0,0 +1,312 @@ +# ERRORS +# ================================================================================================= + +# Unknown version in account ID. +const.ERR_ACCOUNT_ID_UNKNOWN_VERSION=0x00020057 + +# Epoch must be less than u16::MAX (0xffff). +const.ERR_ACCOUNT_ID_EPOCH_MUST_BE_LESS_THAN_U16_MAX=0x00020058 + +# Unknown account storage mode in account ID. +const.ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE=0x00020059 + +# Least significant byte of the account ID suffix must be zero. +const.ERR_ACCOUNT_ID_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO=0x00020005 + +# Provided storage slot index is out of bounds +const.ERR_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS=0x0002000D + +# CONSTANTS +# ================================================================================================= + +# Bit pattern for a faucet account, after the account type mask has been applied. +const.FAUCET_ACCOUNT=0x20 # 0b10_0000 + +# Bit pattern for an account w/ updatable code, after the account type mask has been applied. +const.REGULAR_ACCOUNT_UPDATABLE_CODE=0x10 # 0b01_0000 + +# Bit pattern for an account w/ immutable code, after the account type mask has been applied. +const.REGULAR_ACCOUNT_IMMUTABLE_CODE=0 # 0b00_0000 + +# Bit pattern for a fungible faucet w/ immutable code, after the account type mask has been applied. +const.FUNGIBLE_FAUCET_ACCOUNT=0x20 # 0b10_0000 + +# Bit pattern for a non-fungible faucet w/ immutable code, after the account type mask has been +# applied. +const.NON_FUNGIBLE_FAUCET_ACCOUNT=0x30 # 0b11_0000 + +# Given the least significant 32 bits of an account id's first felt, this mask defines the bits used +# to determine the account type. +const.ACCOUNT_ID_TYPE_MASK_U32=0x30 # 0b11_0000 + +# Given the least significant 32 bits of an account id's first felt, this mask defines the bits used +# to determine the account version. +const.ACCOUNT_VERSION_MASK_U32=0x0f # 0b1111 + +# Given the least significant 32 bits of an account ID's first felt, this mask defines the bits used +# to determine the account storage mode. +const.ACCOUNT_ID_STORAGE_MODE_MASK_U32=0xC0 # 0b1100_0000 + +# Given the least significant 32 bits of an account ID's first felt with the storage mode mask +# applied, this value defines the public storage mode. +const.ACCOUNT_ID_STORAGE_MODE_PUBLIC_U32=0 # 0b0000_0000 + +# Given the least significant 32 bits of an account ID's first felt with the storage mode mask +# applied, this value defines the private storage mode. +const.ACCOUNT_ID_STORAGE_MODE_PRIVATE_U32=0x80 # 0b1000_0000 + +# PROCEDURES +# ================================================================================================= + +#! Returns a boolean indicating whether the account is a fungible faucet. +#! +#! Inputs: [acct_id_prefix] +#! Outputs: [is_fungible_faucet] +#! +#! Where: +#! - acct_id_prefix is the prefix of the account ID. +#! - is_fungible_faucet is a boolean indicating whether the account is a fungible faucet. +export.is_fungible_faucet + exec.id_type push.FUNGIBLE_FAUCET_ACCOUNT eq + # => [is_fungible_faucet] +end + +#! Returns a boolean indicating whether the account is a non-fungible faucet. +#! +#! Inputs: [acct_id_prefix] +#! Outputs: [is_non_fungible_faucet] +#! +#! Where: +#! - acct_id_prefix is the prefix of the account ID. +#! - is_non_fungible_faucet is a boolean indicating whether the account is a non-fungible faucet. +export.is_non_fungible_faucet + exec.id_type push.NON_FUNGIBLE_FAUCET_ACCOUNT eq + # => [is_non_fungible_faucet] +end + +#! Returns a boolean indicating whether the given account_ids are equal. +#! +#! Inputs: [acct_id_prefix, acct_id_suffix, other_acct_id_prefix, other_acct_id_suffix] +#! Outputs: [is_id_equal] +#! +#! Where: +#! - acct_id_{prefix,suffix} are the prefix and suffix felts of an account ID. +#! - other_acct_id_{prefix,suffix} are the prefix and suffix felts of the other account ID to compare against. +#! - is_id_equal is a boolean indicating whether the account IDs are equal. +export.is_equal + movup.2 eq + # => [is_prefix_equal, acct_id_suffix, other_acct_id_suffix] + movdn.2 eq + # => [is_suffix_equal, is_prefix_equal] + and + # => [is_id_equal] +end + +#! Returns a boolean indicating whether the account is a faucet. +#! +#! Inputs: [acct_id_prefix] +#! Outputs: [is_faucet] +#! +#! Where: +#! - acct_id_prefix is the prefix of the account ID. +#! - is_faucet is a boolean indicating whether the account is a faucet. +export.is_faucet + u32split drop push.FAUCET_ACCOUNT u32and eq.0 not + # => [is_faucet] +end + +#! Returns a boolean indicating whether the account is a regular updatable account. +#! +#! Inputs: [acct_id_prefix] +#! Outputs: [is_updatable_account] +#! +#! Where: +#! - acct_id_prefix is the prefix of the account ID. +#! - is_updatable_account is a boolean indicating whether the account is a regular updatable +#! account. +export.is_updatable_account + exec.id_type push.REGULAR_ACCOUNT_UPDATABLE_CODE eq + # => [is_updatable_account] +end + +#! Returns a boolean indicating whether the account is a regular immutable account. +#! +#! Inputs: [acct_id_prefix] +#! Outputs: [is_immutable_account] +#! +#! Where: +#! - acct_id_prefix is the prefix of the account ID. +#! - is_immutable_account is a boolean indicating whether the account is a regular immutable +#! account. +export.is_immutable_account + exec.id_type push.REGULAR_ACCOUNT_IMMUTABLE_CODE eq + # => [is_immutable_account] +end + +#! Validates an account ID. Note that this does not validate anything about the account type, +#! since any bit pattern is a valid account type. +#! +#! Inputs: [account_id_prefix, account_id_suffix] +#! Outputs: [] +#! +#! Where: +#! - account_id_{prefix,suffix} are the prefix and suffix felts of the account ID. +#! +#! Panics if: +#! - account_id_prefix does not contain version zero. +#! - account_id_prefix does not contain either the public or private storage mode. +#! - account_id_suffix contains an anchor epoch that is greater or equal to 2^16. +#! - account_id_suffix does not have its lower 8 bits set to zero. +export.validate + # Validate version in prefix. For now only version 0 is supported. + # --------------------------------------------------------------------------------------------- + + dup exec.id_version + # => [id_version, account_id_prefix, account_id_suffix] + assertz.err=ERR_ACCOUNT_ID_UNKNOWN_VERSION + # => [account_id_prefix, account_id_suffix] + + # Validate storage mode in prefix. + # --------------------------------------------------------------------------------------------- + + u32split drop + # => [account_id_prefix_lo, account_id_suffix] + u32and.ACCOUNT_ID_STORAGE_MODE_MASK_U32 dup eq.ACCOUNT_ID_STORAGE_MODE_PRIVATE_U32 + # => [is_private_storage_mode, id_storage_mode_masked, account_id_suffix] + swap eq.ACCOUNT_ID_STORAGE_MODE_PUBLIC_U32 + # => [is_public_storage_mode, is_private_storage_mode, account_id_suffix] + or assert.err=ERR_ACCOUNT_ID_UNKNOWN_STORAGE_MODE + # => [account_id_suffix] + + # Validate anchor epoch is less than u16::MAX (0xffff) in suffix. + # --------------------------------------------------------------------------------------------- + + dup exec.id_anchor_epoch + # => [anchor_epoch, account_id_suffix] + lt.0xffff assert.err=ERR_ACCOUNT_ID_EPOCH_MUST_BE_LESS_THAN_U16_MAX + # => [account_id_suffix] + + # Validate lower 8 bits of suffix are zero. + # --------------------------------------------------------------------------------------------- + + u32split drop u32and.0xff eq.0 + # => [is_least_significant_byte_zero] + assert.err=ERR_ACCOUNT_ID_LEAST_SIGNIFICANT_BYTE_MUST_BE_ZERO + # => [] +end + +#! Shapes the suffix so it meets the requirements of the account ID, by overwriting the +#! upper 16 bits with the anchor epoch and setting the lower 8 bits to zero. +#! +#! Inputs: [seed_digest_suffix, anchor_epoch] +#! Outputs: [account_id_suffix] +#! +#! Where: +#! - seed_digest_suffix is the suffix of the digest that should be shaped into the suffix +#! of an account ID. +#! - account_id_suffix is the suffix of an account ID. +#! - anchor_epoch is the epoch number to which this account ID is anchored. +export.shape_suffix + u32split + # => [seed_digest_suffix_hi, seed_digest_suffix_lo, anchor_epoch] + + # clear epoch bits in hi part so we can set them later + u32and.0x0000ffff swap + # => [seed_digest_suffix_lo, seed_digest_suffix_hi', anchor_epoch] + + # clear lower 8 bits of the lo part + u32and.0xffffff00 swap.2 + # => [anchor_epoch, seed_digest_suffix_hi', seed_digest_suffix_lo'] + + # assert epoch is not 2^16 + # this is technically optional as we will compare this id with the provided one for which + # this property was already checked, but since this check is cheap we include it anyway + dup eq.0xffff assertz.err=ERR_ACCOUNT_ID_EPOCH_MUST_BE_LESS_THAN_U16_MAX + # => [anchor_epoch, seed_digest_suffix_hi', seed_digest_suffix_lo'] + + # shift epoch left by 16 bits and set epoch bits on hi part + u32shl.16 u32or + # => [seed_digest_suffix_hi'', seed_digest_suffix_lo'] + + # reassemble the suffix by multiplying the hi part with 2^32 and adding the lo part + mul.0x0100000000 add + # => [account_id_suffix] +end + +# HELPER PROCEDURES +# ================================================================================================= + +#! Extracts the block number of the anchor block from the suffix of an account ID. +#! +#! Inputs: [account_id_suffix] +#! Outputs: [anchor_block_num] +#! +#! Where: +#! - account_id_suffix is the suffix of an account ID. +#! - anchor_block_num is the number of the block to which this account ID is anchored. +proc.id_anchor_block_num + # extract the upper 32 bits + u32split swap drop + # => [account_id_suffix_hi] + + # to get the epoch's block number we would have to multiply the epoch in the account ID by 2^16 + # since the epoch is already in the upper 16 bits of the u32, we can simply zero out the + # lower 16 bits to achieve the same result. + u32and.0xffff0000 + # => [anchor_block_num] +end + +#! Extracts the epoch from the suffix of an account ID. +#! +#! Inputs: [account_id_suffix] +#! Outputs: [anchor_epoch] +#! +#! Where: +#! - account_id_suffix is the suffix of an account ID. +#! - anchor_epoch is the epoch number to which this account ID is anchored. +proc.id_anchor_epoch + # extract the upper 32 bits + u32split swap drop + # => [account_id_suffix_hi] + + # shift the upper 16 bits to the right to produce the epoch + u32shr.16 + # => [anchor_epoch] +end + +#! Extracts the account ID version from the prefix of an account ID. +#! +#! Inputs: [account_id_prefix] +#! Outputs: [id_version] +#! +#! Where: +#! - account_id_prefix is the prefix of an account ID. +#! - id_version is the version number of the ID. +proc.id_version + # extract the lower 32 bits + u32split drop + # => [account_id_prefix_lo] + + # mask out the version + u32and.ACCOUNT_VERSION_MASK_U32 + # => [id_version] +end + +#! Returns the least significant half of an account ID prefix with the account type bits masked out. +#! +#! The account type can be obtained by comparing this value with the following constants: +#! - REGULAR_ACCOUNT_UPDATABLE_CODE +#! - REGULAR_ACCOUNT_IMMUTABLE_CODE +#! - FUNGIBLE_FAUCET_ACCOUNT +#! - NON_FUNGIBLE_FAUCET_ACCOUNT +#! +#! Inputs: [acct_id_prefix] +#! Outputs: [acct_type] +#! +#! Where: +#! - acct_id_prefix is the prefix of the account ID. +#! - acct_type is the account type. +proc.id_type + u32split drop push.ACCOUNT_ID_TYPE_MASK_U32 u32and + # => [acct_type] +end diff --git a/miden-lib/asm/shared/util/asset.masm b/miden-lib/asm/shared/util/asset.masm new file mode 100644 index 000000000..2fcfc0af4 --- /dev/null +++ b/miden-lib/asm/shared/util/asset.masm @@ -0,0 +1,19 @@ +# CONSTANTS +# ================================================================================================= + +const.FUNGIBLE_ASSET_MAX_AMOUNT=9223372036854775807 + +# PROCEDURES +# ================================================================================================= + +#! Returns the maximum amount of a fungible asset. +#! +#! Inputs: [] +#! Outputs: [fungible_asset_max_amount] +#! +#! Where: +#! - fungible_asset_max_amount is the maximum amount of a fungible asset. +export.get_fungible_asset_max_amount + push.FUNGIBLE_ASSET_MAX_AMOUNT + # => [fungible_asset_max_amount] +end diff --git a/miden-lib/asm/shared/util/note.masm b/miden-lib/asm/shared/util/note.masm new file mode 100644 index 000000000..dc1bc2185 --- /dev/null +++ b/miden-lib/asm/shared/util/note.masm @@ -0,0 +1,19 @@ +# CONSTANTS +# ================================================================================================= + +# The maximum number of input values associated with a single note. +const.MAX_INPUTS_PER_NOTE=128 + +# PROCEDURES +# ================================================================================================= + +#! Returns the max allowed number of input values per note. +#! +#! Inputs: [] +#! Outputs: [max_inputs_per_note] +#! +#! Where: +#! - max_inputs_per_note is the max inputs per note. +export.get_max_inputs_per_note + push.MAX_INPUTS_PER_NOTE +end diff --git a/miden-lib/build.rs b/miden-lib/build.rs index e9234e590..3239008d9 100644 --- a/miden-lib/build.rs +++ b/miden-lib/build.rs @@ -27,9 +27,9 @@ const ASM_DIR: &str = "asm"; const ASM_MIDEN_DIR: &str = "miden"; const ASM_NOTE_SCRIPTS_DIR: &str = "note_scripts"; const ASM_ACCOUNT_COMPONENTS_DIR: &str = "account_components"; +const SHARED_DIR: &str = "shared"; const ASM_TX_KERNEL_DIR: &str = "kernels/transaction"; const KERNEL_V0_RS_FILE: &str = "src/transaction/procedures/kernel_v0.rs"; - const KERNEL_ERRORS_FILE: &str = "src/errors/tx_kernel_errors.rs"; // PRE-PROCESSING @@ -56,6 +56,7 @@ fn main() -> Result<()> { // set target directory to {OUT_DIR}/assets let target_dir = Path::new(&build_dir).join(ASSETS_DIR); + // compile transaction kernel let mut assembler = compile_tx_kernel(&source_dir.join(ASM_TX_KERNEL_DIR), &target_dir.join("kernels"))?; @@ -101,7 +102,12 @@ fn main() -> Result<()> { /// - {target_dir}/tx_kernel.masb -> contains the executable compiled from main.masm. /// - src/transaction/procedures/kernel_v0.rs -> contains the kernel procedures table. fn compile_tx_kernel(source_dir: &Path, target_dir: &Path) -> Result { - let assembler = build_assembler(None)?; + let shared_path = Path::new(ASM_DIR).join(SHARED_DIR); + let kernel_namespace = LibraryNamespace::new("kernel").expect("namespace should be valid"); + + let mut assembler = build_assembler(None)?; + // add the shared modules to the kernel lib under the kernel::util namespace + assembler.add_modules_from_dir(kernel_namespace.clone(), &shared_path)?; // assemble the kernel library and write it to the "tx_kernel.masl" file let kernel_lib = KernelLibrary::from_dir( @@ -120,8 +126,9 @@ fn compile_tx_kernel(source_dir: &Path, target_dir: &Path) -> Result // assemble the kernel program and write it the "tx_kernel.masb" file let mut main_assembler = assembler.clone(); - let namespace = LibraryNamespace::new("kernel").expect("invalid namespace"); - main_assembler.add_modules_from_dir(namespace, &source_dir.join("lib"))?; + main_assembler.add_modules_from_dir(kernel_namespace.clone(), &source_dir.join("lib"))?; + // add the shared modules to the kernel lib under the kernel::util namespace + main_assembler.add_modules_from_dir(kernel_namespace, &shared_path)?; let main_file_path = source_dir.join("main.masm").clone(); let kernel_main = main_assembler.assemble_program(main_file_path)?; @@ -131,12 +138,19 @@ fn compile_tx_kernel(source_dir: &Path, target_dir: &Path) -> Result #[cfg(any(feature = "testing", test))] { + let mut kernel_lib_assembler = assembler.clone(); // Build kernel as a library and save it to file. // This is needed in test assemblers to access individual procedures which would otherwise // be hidden when using KernelLibrary (api.masm) - let namespace = "kernel".parse::().expect("invalid base namespace"); + let kernel_namespace = + "kernel".parse::().expect("invalid base namespace"); + + // add the shared modules to the kernel lib under the kernel::util namespace + kernel_lib_assembler.add_modules_from_dir(kernel_namespace.clone(), &shared_path)?; + let test_lib = - Library::from_dir(source_dir.join("lib"), namespace, assembler.clone()).unwrap(); + Library::from_dir(source_dir.join("lib"), kernel_namespace, kernel_lib_assembler) + .unwrap(); let masb_file_path = target_dir.join("kernel_library").with_extension(Library::LIBRARY_EXTENSION); @@ -225,12 +239,16 @@ fn parse_proc_offsets(filename: impl AsRef) -> Result Result { let source_dir = source_dir.join(ASM_MIDEN_DIR); + let shared_path = Path::new(ASM_DIR).join(SHARED_DIR); + + let miden_namespace = "miden".parse::().expect("invalid base namespace"); + // add the shared modules to the kernel lib under the miden::util namespace + assembler.add_modules_from_dir(miden_namespace.clone(), &shared_path)?; - let namespace = "miden".parse::().expect("invalid base namespace"); - let miden_lib = Library::from_dir(source_dir, namespace, assembler)?; + let miden_lib = Library::from_dir(source_dir, miden_namespace, assembler)?; let output_file = target_dir.join("miden").with_extension(Library::LIBRARY_EXTENSION); miden_lib.write_to_file(output_file).into_diagnostic()?;