Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
- [BREAKING] Renamed `note::build_recipient_hash` to `note::compute_recipient` and `note::build_recipient` to `note::compute_and_store_recipient` ([#2875](https://github.com/0xMiden/protocol/issues/2875)).
- [BREAKING] Merged `BasicFungibleFaucet` and `NetworkFungibleFaucet` ([#2890](https://github.com/0xMiden/protocol/pull/2890)).
- [BREAKING] Renamed `NoteMetadata` to `PartialNoteMetadata` and renamed `NoteMetadataHeader` to `NoteMetadata` ([#2887](https://github.com/0xMiden/protocol/pull/2887)).
- [BREAKING] Hashed `AssetVaultKey` before insertion into the asset vault SMT so non-fungible assets issued by the same faucet no longer share a leaf ([#2912](https://github.com/0xMiden/protocol/pull/2912)).
- [BREAKING] Renamed account ID version 0 to version 1 and made encoded version 0 invalid ([#2842](https://github.com/0xMiden/protocol/issues/2842)).
- [BREAKING] Changed note metadata version 1 to encode as `1`, leaving encoded version `0` invalid.
- Documented the `miden::protocol::account_id` module in the protocol library docs ([#2607](https://github.com/0xMiden/protocol/issues/2607)).
Expand Down
47 changes: 42 additions & 5 deletions crates/miden-protocol/asm/kernels/transaction/lib/asset_vault.masm
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
use miden::core::collections::smt
use miden::core::crypto::hashes::poseidon2

use $kernel::asset
use $kernel::fungible_asset
use $kernel::non_fungible_asset

# ASSET KEY HASHING
#! Hashes the raw asset vault key into the key used by the asset vault SMT.
#!
#! See [`AssetVaultKey::to_smt_key`] in Rust for the rationale.
#!
Comment thread
partylikeits1983 marked this conversation as resolved.
#! Inputs: [ASSET_KEY]
#! Outputs: [HASHED_ASSET_KEY]
proc hash_asset_key
exec.poseidon2::hash
# => [HASHED_ASSET_KEY]
end

# ERRORS
# =================================================================================================

Expand All @@ -28,12 +41,16 @@ const ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND="failed to remove non-exi
#! - ASSET_KEY is the asset vault key of the asset to fetch.
#! - ASSET_VALUE is the value of the asset from the vault, which can be the EMPTY_WORD if it isn't present.
pub proc get_asset
# hash the asset vault key before using it as the SMT key
exec.hash_asset_key
# => [HASHED_ASSET_KEY, vault_root_ptr]

# load the asset vault root from memory
padw movup.8 mem_loadw_le
# => [ASSET_VAULT_ROOT, ASSET_KEY]
# => [ASSET_VAULT_ROOT, HASHED_ASSET_KEY]

swapw
# => [ASSET_KEY, ASSET_VAULT_ROOT]
# => [HASHED_ASSET_KEY, ASSET_VAULT_ROOT]

# lookup asset
exec.smt::get swapw dropw
Expand Down Expand Up @@ -64,16 +81,20 @@ end
#! - ASSET_KEY is the asset vault key of the asset to fetch.
#! - ASSET_VALUE is the retrieved asset.
pub proc peek_asset
# hash the asset vault key before using it as the SMT key
exec.hash_asset_key
# => [HASHED_ASSET_KEY, vault_root_ptr]

# load the asset vault root from memory
padw movup.8 mem_loadw_le
# => [ASSET_VAULT_ROOT, ASSET_KEY]
# => [ASSET_VAULT_ROOT, HASHED_ASSET_KEY]

swapw
# => [ASSET_KEY, ASSET_VAULT_ROOT]
# => [HASHED_ASSET_KEY, ASSET_VAULT_ROOT]

# lookup asset
exec.smt::peek
# OS => [ASSET_KEY, ASSET_VAULT_ROOT]
# OS => [HASHED_ASSET_KEY, ASSET_VAULT_ROOT]
# AS => [ASSET_VALUE]

dropw
Expand Down Expand Up @@ -153,6 +174,10 @@ pub proc add_fungible_asset
movdnw.2
# => [MERGED_ASSET_VALUE, ASSET_KEY, VAULT_ROOT, CUR_VAULT_VALUE, MERGED_ASSET_VALUE]

# hash the asset key before using it as the SMT key
swapw exec.hash_asset_key swapw
# => [MERGED_ASSET_VALUE, HASHED_ASSET_KEY, VAULT_ROOT, CUR_VAULT_VALUE, MERGED_ASSET_VALUE]

# update asset in vault
exec.smt::set
# => [PREV_VAULT_VALUE, NEW_VAULT_ROOT, CUR_VAULT_VALUE, MERGED_ASSET_VALUE]
Expand Down Expand Up @@ -192,6 +217,10 @@ pub proc add_non_fungible_asset
dupw.2
# => [ASSET_VALUE, ASSET_KEY, VAULT_ROOT, ASSET_VALUE, vault_root_ptr]

# hash the asset key before using it as the SMT key
swapw exec.hash_asset_key swapw
# => [ASSET_VALUE, HASHED_ASSET_KEY, VAULT_ROOT, ASSET_VALUE, vault_root_ptr]

# insert asset into vault
exec.smt::set
# => [OLD_VAL, VAULT_ROOT', ASSET_VALUE, vault_root_ptr]
Expand Down Expand Up @@ -303,6 +332,10 @@ pub proc remove_fungible_asset
movdnw.2
# => [REMAINING_ASSET_VALUE, ASSET_KEY, VAULT_ROOT, PEEKED_ASSET_VALUE, vault_root_ptr]

# hash the asset key before using it as the SMT key
swapw exec.hash_asset_key swapw
# => [REMAINING_ASSET_VALUE, HASHED_ASSET_KEY, VAULT_ROOT, PEEKED_ASSET_VALUE, vault_root_ptr]

# update asset in vault and assert the old value is equivalent to the peeked value provided
# via peek_asset
exec.smt::set
Expand Down Expand Up @@ -347,6 +380,10 @@ pub proc remove_non_fungible_asset
swapw padw
# => [EMPTY_WORD, ASSET_KEY, VAULT_ROOT, ASSET_VALUE, vault_root_ptr]

# hash the asset key before using it as the SMT key
swapw exec.hash_asset_key swapw
# => [EMPTY_WORD, HASHED_ASSET_KEY, VAULT_ROOT, ASSET_VALUE, vault_root_ptr]

# insert empty word into the vault to remove the asset
exec.smt::set
# => [REMOVED_ASSET_VALUE, NEW_VAULT_ROOT, ASSET_VALUE, vault_root_ptr]
Expand Down
22 changes: 18 additions & 4 deletions crates/miden-protocol/asm/kernels/transaction/lib/epilogue.masm
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,36 @@ const EPILOGUE_AUTH_PROC_START_EVENT=event("miden::protocol::epilogue::auth_proc
# Event emitted to signal that an execution of the authentication procedure has ended.
const EPILOGUE_AUTH_PROC_END_EVENT=event("miden::protocol::epilogue::auth_proc_end")

# An additional number of cyclces to account for the number of cycles that smt::set will take when
# An additional number of cycles to account for the number of cycles that smt::set will take when
# removing the computed fee from the asset vault.
# Theoretically, this can safely be set to worst_case_cycles - best_case_cycles of smt::set. That's
# because we can assume that the measured number of cycles already contains at least the best case
# number of cycles, and so we only need to add the difference.
const SMT_SET_ADDITIONAL_CYCLES=250

# An upper-bound estimate of the cycles needed to hash the asset vault key before the fee-removing
# smt::set. This cost is incurred only when a fee is actually removed from the asset vault, and is
# kept separate from SMT_SET_ADDITIONAL_CYCLES (which models smt::set's own best/worst-case spread).
# It is additive rather than double-counted: the lowest-observed NUM_POST_COMPUTE_FEE_CYCLES below
# comes from a zero-fee transaction that skips fee removal entirely, and so excludes this hashing.
const VAULT_KEY_HASH_CYCLES=50

# The number of cycles the epilogue is estimated to take after compute_fee has been executed,
# including an unknown cycle number of the above-mentioned call to smt::set. It is safe to assume
# that this includes at least smt::set's best case number of cycles.
# This can be _estimated_ using the transaction measurements on ExecutedTransaction and can be set
# to the lowest observed value.
# to the lowest observed value (which comes from a zero-fee transaction that skips fee removal).
const NUM_POST_COMPUTE_FEE_CYCLES=608

# The number of cycles the epilogue is estimated to take after compute_fee has been executed.
const ESTIMATED_AFTER_COMPUTE_FEE_CYCLES=NUM_POST_COMPUTE_FEE_CYCLES+SMT_SET_ADDITIONAL_CYCLES
# The number of cycles the epilogue is estimated to take after compute_fee has been executed. This
# must remain an upper bound on the real post-compute_fee cycle count, otherwise the verification
# fee is undercharged. The most recently observed worst case is 863 cycles, leaving 45 cycles of
# margin against this 908-cycle budget; the margin is empirical, so re-measure when the epilogue or
# smt::set changes. The fee-removal smt::set cost cannot be inflated by an adversary: vault keys are
# hashed before insertion (see AssetVaultKey::hash), so leaf indices are uniformly distributed and
# entries cannot be concentrated into the fee asset's leaf without breaking the hash's collision
# resistance. The multi-leaf smt::set worst case is not yet exercised (see test_fee.rs TODO).
const ESTIMATED_AFTER_COMPUTE_FEE_CYCLES=NUM_POST_COMPUTE_FEE_CYCLES+SMT_SET_ADDITIONAL_CYCLES+VAULT_KEY_HASH_CYCLES

# OUTPUT NOTES PROCEDURES
# =================================================================================================
Expand Down
9 changes: 8 additions & 1 deletion crates/miden-protocol/src/asset/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,14 @@ mod asset_composition;
pub use asset_composition::AssetComposition;

mod vault;
pub use vault::{AssetId, AssetVault, AssetVaultKey, AssetWitness, PartialVault};
pub use vault::{
AssetId,
AssetVault,
AssetVaultKey,
AssetVaultKeyHash,
AssetWitness,
PartialVault,
};

// ASSET
// ================================================================================================
Expand Down
Loading
Loading