Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
- Added standardized `NetworkAccountNoteAllowlist` slot for detecting network accounts ([#2883](https://github.com/0xMiden/protocol/pull/2883)).
- [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.
- [BREAKING] Added `NetworkAccount` wrapper for convenient network account identification ([#2915](https://github.com/0xMiden/protocol/pull/2915)).
Expand Down
73 changes: 58 additions & 15 deletions crates/miden-protocol/asm/kernels/transaction/lib/asset_vault.masm
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use miden::core::collections::smt
use miden::core::crypto::hashes::poseidon2

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

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

const ERR_VAULT_ADD_FUNGIBLE_ASSET_FAILED_INITIAL_VALUE_INVALID="failed to add fungible asset to the asset vault due to the initial value being invalid"
Expand All @@ -28,12 +29,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 +69,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,8 +162,8 @@ pub proc add_fungible_asset
movdnw.2
# => [MERGED_ASSET_VALUE, ASSET_KEY, VAULT_ROOT, CUR_VAULT_VALUE, MERGED_ASSET_VALUE]

# update asset in vault
exec.smt::set
# hash the asset key and update the asset in the vault
exec.set_asset
# => [PREV_VAULT_VALUE, NEW_VAULT_ROOT, CUR_VAULT_VALUE, MERGED_ASSET_VALUE]

# assert PREV_VAULT_VALUE = CUR_VAULT_VALUE to make sure peek_asset returned the correct asset
Expand Down Expand Up @@ -192,8 +201,8 @@ pub proc add_non_fungible_asset
dupw.2
# => [ASSET_VALUE, ASSET_KEY, VAULT_ROOT, ASSET_VALUE, vault_root_ptr]

# insert asset into vault
exec.smt::set
# hash the asset key and insert the asset into the vault
exec.set_asset
# => [OLD_VAL, VAULT_ROOT', ASSET_VALUE, vault_root_ptr]

# assert old value was empty
Expand Down Expand Up @@ -303,9 +312,9 @@ pub proc remove_fungible_asset
movdnw.2
# => [REMAINING_ASSET_VALUE, 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
# hash the asset key and update the asset in the vault; the old value is asserted below to be
# equivalent to the peeked value provided via peek_asset
exec.set_asset
# => [OLD_VALUE, NEW_VAULT_ROOT, PEEKED_ASSET_VALUE, vault_root_ptr]

# assert OLD_VALUE == PEEKED_ASSET
Expand Down Expand Up @@ -347,8 +356,8 @@ pub proc remove_non_fungible_asset
swapw padw
# => [EMPTY_WORD, ASSET_KEY, VAULT_ROOT, ASSET_VALUE, vault_root_ptr]

# insert empty word into the vault to remove the asset
exec.smt::set
# hash the asset key and insert the empty word into the vault to remove the asset
exec.set_asset
# => [REMOVED_ASSET_VALUE, NEW_VAULT_ROOT, ASSET_VALUE, vault_root_ptr]

movupw.2 assert_eqw.err=ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND
Expand Down Expand Up @@ -396,3 +405,37 @@ pub proc remove_asset
# => [REMAINING_ASSET_VALUE]
end
end

# HELPER PROCEDURES
# =================================================================================================

#! Hashes the raw asset vault key into the key used by the asset vault SMT.
#!
#! See [`AssetVaultKey::hash`] in Rust for the rationale.
#!
#! Inputs: [ASSET_KEY]
#! Outputs: [HASHED_ASSET_KEY]
proc hash_asset_key
exec.poseidon2::hash
# => [HASHED_ASSET_KEY]
end

#! Hashes the raw asset vault key and writes ASSET_VALUE into the asset vault SMT at the hashed key,
#! returning the previous value stored there.
#!
#! Inputs: [ASSET_VALUE, ASSET_KEY, VAULT_ROOT, ...]
#! Outputs: [OLD_VALUE, NEW_VAULT_ROOT, ...]
#!
#! Where:
#! - ASSET_KEY is the raw (unhashed) asset vault key.
#! - ASSET_VALUE is the value to write into the vault at the hashed key.
#! - VAULT_ROOT is the current root of the asset vault SMT.
#! - OLD_VALUE is the value previously stored at the hashed key.
#! - NEW_VAULT_ROOT is the root of the asset vault SMT after the write.
proc set_asset
swapw exec.hash_asset_key swapw
# => [ASSET_VALUE, HASHED_ASSET_KEY, VAULT_ROOT, ...]

exec.smt::set
# => [OLD_VALUE, NEW_VAULT_ROOT, ...]
end
Comment on lines +423 to +441
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: We don't use the ... syntax in stack comments anymore.

19 changes: 15 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,33 @@ 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
# Upper bound on the post-compute_fee cycle count; it must stay an upper bound or the verification
# fee is undercharged. Worst case observed is 863 cycles (45 of margin); re-measure when the
# epilogue or smt::set changes. An adversary cannot inflate the fee-removal smt::set cost: vault
# keys are hashed before insertion (see AssetVaultKey::hash). 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