Skip to content
Merged
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
27 changes: 26 additions & 1 deletion crates/miden-protocol/asm/kernels/transaction/lib/asset.masm
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use $kernel::account_id
use $kernel::fungible_asset
use $kernel::non_fungible_asset
use $kernel::util::asset->util_asset

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

const ERR_VAULT_ASSET_KEY_ACCOUNT_ID_MUST_BE_FAUCET="account ID in asset vault key must be either of type fungible or non-fungible faucet"
Expand Down Expand Up @@ -65,6 +66,30 @@ pub proc validate_key
# => [ASSET_KEY]
end

#! Validates the issuer (faucet ID) and metadata in an asset vault key.
#!
#! Inputs: [ASSET_KEY]
#! Outputs: [ASSET_KEY]
#!
#! Where:
#! - ASSET_KEY is the vault key of the asset to validate.
#!
#! Panics if:
#! - the asset metadata is invalid (not 0 or 1).
#! - the faucet ID in the key is not a valid account ID.
pub proc validate_issuer
# => [asset_id_suffix, asset_id_prefix, faucet_id_suffix_and_metadata, faucet_id_prefix]

dup.3 dup.3 exec.util_asset::split_suffix_and_metadata
# => [asset_metadata, faucet_id_suffix, faucet_id_prefix, ASSET_KEY]

exec.util_asset::validate_metadata
# => [faucet_id_suffix, faucet_id_prefix, ASSET_KEY]

exec.account_id::validate
# => [ASSET_KEY]
end

#! Returns a boolean indicating whether the asset is non-fungible.
#!
#! Inputs: [ASSET_KEY]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,10 @@ end
#!
#! Panics if:
#! - the asset key's account ID is not valid.
#! - the asset key's metadata is not valid.
#! - the asset key's faucet ID is not a fungible one.
pub proc validate_key
exec.asset::key_to_faucet_id
exec.account_id::validate
exec.asset::validate_issuer
# => [ASSET_KEY]

exec.asset::is_fungible_asset_key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ end
#!
#! Panics if:
#! - the asset key's account ID is not valid.
#! - the asset key's metadata is not valid.
#! - the asset key's faucet ID is not a non-fungible one.
pub proc validate_key
exec.asset::key_to_faucet_id
exec.account_id::validate
exec.asset::validate_issuer
# => [ASSET_KEY]

exec.asset::is_non_fungible_asset_key
Expand Down
21 changes: 17 additions & 4 deletions crates/miden-protocol/asm/shared_utils/util/asset.masm
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ end
#! - faucet_id_suffix_and_metadata is the faucet ID suffix merged with the asset metadata.
#! - faucet_id_suffix is the suffix of the account ID.
#! - asset_metadata is the asset metadata.
proc split_suffix_and_metadata
pub proc split_suffix_and_metadata
u32split
# => [suffix_metadata_lo, suffix_metadata_hi]

Expand All @@ -312,6 +312,20 @@ proc split_suffix_and_metadata
# => [asset_metadata, faucet_id_suffix]
end

#! Validates that asset metadata is well formed and consumes it.
#!
#! Inputs: [asset_metadata]
#! Outputs: []
#!
#! Panics if:
#! - asset_metadata is not a valid u32 or exceeds CALLBACKS_ENABLED.
pub proc validate_metadata
u32assert.err=ERR_VAULT_INVALID_ENABLE_CALLBACKS
u32lte.CALLBACKS_ENABLED
assert.err=ERR_VAULT_INVALID_ENABLE_CALLBACKS
# => []
end

#! Creates asset metadata from the provided inputs.
#!
#! Inputs: [enable_callbacks]
Expand All @@ -324,9 +338,8 @@ end
#! Panics if:
#! - enable_callbacks is not 0 or 1.
proc create_metadata
u32assert.err=ERR_VAULT_INVALID_ENABLE_CALLBACKS
dup u32lte.CALLBACKS_ENABLED
assert.err=ERR_VAULT_INVALID_ENABLE_CALLBACKS
# for now, enable_callbacks is identical to asset_metadata
dup exec.validate_metadata
# => [asset_metadata]
end

Expand Down
14 changes: 12 additions & 2 deletions crates/miden-protocol/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,12 +352,21 @@ fn copy_shared_modules<T: AsRef<Path>>(source_dir: T) -> Result<()> {
/// The generated files are written to `build_dir` (i.e. `OUT_DIR`) and included via `include!`
/// in the source.
fn generate_error_constants(asm_source_dir: &Path, build_dir: &str) -> Result<()> {
// Shared utils errors
// For now these are duplicated in the tx kernel and protocol error module.
// ------------------------------------------

let shared_utils_dir = asm_source_dir.join(SHARED_UTILS_DIR);
let shared_utils_errors = shared::extract_all_masm_errors(&shared_utils_dir)
.context("failed to extract all masm errors")?;

// Transaction kernel errors
// ------------------------------------------

let tx_kernel_dir = asm_source_dir.join(ASM_TX_KERNEL_DIR);
let errors = shared::extract_all_masm_errors(&tx_kernel_dir)
let mut errors = shared::extract_all_masm_errors(&tx_kernel_dir)
.context("failed to extract all masm errors")?;
errors.extend_from_slice(&shared_utils_errors);
validate_tx_kernel_category(&errors)?;

shared::generate_error_file(
Expand All @@ -373,8 +382,9 @@ fn generate_error_constants(asm_source_dir: &Path, build_dir: &str) -> Result<()
// ------------------------------------------

let protocol_dir = asm_source_dir.join(ASM_PROTOCOL_DIR);
let errors = shared::extract_all_masm_errors(&protocol_dir)
let mut errors = shared::extract_all_masm_errors(&protocol_dir)
.context("failed to extract all masm errors")?;
errors.extend(shared_utils_errors);

shared::generate_error_file(
shared::ErrorModule {
Expand Down
37 changes: 37 additions & 0 deletions crates/miden-testing/src/kernel_tests/tx/test_faucet.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use alloc::sync::Arc;

use miden_protocol::Felt;
use miden_protocol::account::{Account, AccountBuilder, AccountComponent, AccountId, AccountType};
use miden_protocol::assembly::DefaultSourceManager;
use miden_protocol::asset::{
Expand All @@ -14,6 +15,7 @@ use miden_protocol::errors::tx_kernel::{
ERR_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN,
ERR_NON_FUNGIBLE_ASSET_FAUCET_IS_NOT_ORIGIN,
ERR_VAULT_FUNGIBLE_ASSET_AMOUNT_LESS_THAN_AMOUNT_TO_WITHDRAW,
ERR_VAULT_INVALID_ENABLE_CALLBACKS,
ERR_VAULT_NON_FUNGIBLE_ASSET_TO_REMOVE_NOT_FOUND,
};
use miden_protocol::testing::account_id::{
Expand Down Expand Up @@ -151,6 +153,41 @@ async fn test_mint_fungible_asset_inconsistent_faucet_id() -> anyhow::Result<()>
Ok(())
}

/// Tests that minting a fungible asset on a non-faucet account fails when the key has its asset
/// metadata (lower 8 bits) set to u8::MAX.
#[tokio::test]
async fn mint_fungible_asset_fails_on_invalid_asset_metadata() -> anyhow::Result<()> {
let asset = FungibleAsset::mock(50);

let mut vault_key_word = asset.to_key_word();
vault_key_word[2] = Felt::try_from(vault_key_word[2].as_canonical_u64() | u8::MAX as u64)?;

let code = format!(
"
use $kernel::prologue
use mock::faucet

begin
exec.prologue::prepare_transaction
push.{ASSET_VALUE}
push.{ASSET_KEY}
call.faucet::mint
dropw dropw
end
",
ASSET_KEY = vault_key_word,
ASSET_VALUE = asset.to_value_word(),
);

let result = TransactionContextBuilder::with_fungible_faucet(asset.faucet_id().into())
.build()?
.execute_code(&code)
.await;
assert_execution_error!(result, ERR_VAULT_INVALID_ENABLE_CALLBACKS);

Ok(())
}

/// Tests that minting a fungible asset with [`FungibleAsset::MAX_AMOUNT`] + 1 fails.
#[tokio::test]
async fn test_mint_fungible_asset_fails_when_amount_exceeds_max_representable_amount()
Expand Down
Loading