diff --git a/CHANGELOG.md b/CHANGELOG.md index 971d4929ac..f9969d34b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - [BREAKING] AggLayer bridge and faucet account builders now take a concrete `BasicConstantFeePolicy` and fee faucet ID, constructing their `FeePolicyManager` internally ([#3583](https://github.com/0xMiden/protocol/pull/3583)). - [BREAKING] The transaction kernel no longer requires assets with `AssetComposition::None` to have the non-fungible asset layout ([#3624](https://github.com/0xMiden/protocol/pull/3624)). - [BREAKING] Refactored `Asset` into a struct holding `AssetId` and `AssetValue` ([#3625](https://github.com/0xMiden/protocol/pull/3625)). +- [BREAKING] Refactored the presence of an asset callback slot imply an enabled asset callback flag: the transaction kernel rejects new accounts that violate this and `AccountBuilder` derives the flag from the installed callback slots, replacing `with_asset_callbacks` with `enable_asset_callbacks` ([#3658](https://github.com/0xMiden/protocol/pull/3658)). - Moved the transaction kernel API procedures into the kernel's `api` submodule, leaving `exec_kernel_proc` as the only `syscall`-invocable kernel procedure ([#3646](https://github.com/0xMiden/protocol/pull/3646)). - Documented that standard note scripts claim only the assets remaining in a note at consumption time ([#3650](https://github.com/0xMiden/protocol/pull/3650)). - [BREAKING] Moved the kernel data section of the transaction kernel memory to address `0`, so that `exec_kernel_proc` becomes reusable across multiple kernels ([#3655](https://github.com/0xMiden/protocol/pull/3655)). diff --git a/bin/bench-transaction/src/context_setups/network_config.rs b/bin/bench-transaction/src/context_setups/network_config.rs index 279dd0a125..15b0b1fadd 100644 --- a/bin/bench-transaction/src/context_setups/network_config.rs +++ b/bin/bench-transaction/src/context_setups/network_config.rs @@ -20,7 +20,6 @@ use miden_protocol::account::{ AccountComponent, AccountId, AccountType, - AssetCallbackFlag, RoleSymbol, }; use miden_protocol::asset::AssetAmount; @@ -101,7 +100,6 @@ pub fn tx_consume_faucet_policy_config_note_network() -> Result .with_component(faucet) .with_component(Ownable2Step::new(owner.id())) .with_component(Authority::OwnerControlled) - .with_asset_callbacks(AssetCallbackFlag::from(token_policy_manager.has_transfer_policy())) .with_components(token_policy_manager) .with_assets([super::fee_funding_asset()?]); let account = builder.add_account_from_builder( @@ -221,7 +219,6 @@ pub fn tx_consume_min_burn_amount_config_note_network() -> Result Result Vec<&'static StorageSlotName> { - vec![ + let mut slot_names = vec![ FungibleFaucet::token_config_slot(), Ownable2Step::slot_name(), Authority::authority_slot(), @@ -304,7 +304,15 @@ impl AggLayerFaucet { TokenPolicyManager::allowed_burn_policies_slot(), TokenPolicyManager::allowed_send_policies_slot(), TokenPolicyManager::allowed_receive_policies_slot(), - ] + ]; + + // The faucet registers send and receive transfer policies, so its policy manager installs + // the protocol-reserved asset callback slots. Their presence is what makes the account ID + // carry an enabled asset callback flag, so requiring them certifies that the faucet's + // transfer policies can be invoked at all. + slot_names.extend(AssetCallbacks::slot_names()); + + slot_names } } diff --git a/crates/miden-agglayer/src/lib.rs b/crates/miden-agglayer/src/lib.rs index dcc60f2702..44306716c4 100644 --- a/crates/miden-agglayer/src/lib.rs +++ b/crates/miden-agglayer/src/lib.rs @@ -3,7 +3,7 @@ extern crate alloc; use miden_core::{Felt, Word}; -use miden_protocol::account::{AccountBuilder, AccountComponent, AccountId, AssetCallbackFlag}; +use miden_protocol::account::{AccountBuilder, AccountComponent, AccountId}; use miden_protocol::assembly::Path; use miden_protocol::asset::TokenSymbol; use miden_protocol::note::NoteScript; @@ -235,7 +235,6 @@ impl AggLayerFaucet { .active_receive_policy(TransferPolicy::allow_all()) .build(); - let asset_callbacks = AssetCallbackFlag::from(token_policy_manager.has_transfer_policy()); let rbac = RoleBasedAccessControl::builder() .role(RoleConfig::new(RoleBasedAccessControl::admin_role()).with_member(faucet_admin)) .role(RoleConfig::new(AggLayerFaucet::fee_manager_role()).with_member(fee_manager)) @@ -244,7 +243,6 @@ impl AggLayerFaucet { NetworkAccount::builder(seed.into(), AggLayerFaucet::allowed_notes(), fee_policy_manager) .expect("faucet note allowlist is non-empty") - .with_asset_callbacks(asset_callbacks) .with_component(agglayer_component) .with_component(Ownable2Step::new(bridge_account_id)) .with_component(rbac) @@ -261,6 +259,8 @@ impl AggLayerFaucet { #[cfg(test)] mod tests { + use miden_protocol::account::AssetCallbackFlag; + use miden_protocol::asset::AssetCallbacks; use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE; use miden_standards::tx_script::ExpirationTransactionScript; @@ -270,6 +270,32 @@ mod tests { create_existing_bridge_account_with_roles, }; + /// The agglayer faucet registers send and receive transfer policies, so its policy manager + /// installs the protocol-reserved asset callback slots and its account ID must carry an enabled + /// asset callback flag. Without the flag the kernel would never invoke those policies. + #[test] + fn agglayer_faucet_has_asset_callbacks_enabled() { + let id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); + + let faucet = create_existing_agglayer_faucet( + Word::default(), + "AGG", + 6, + Felt::from(1000u32), + Felt::ZERO, + id, + id, + ); + + for slot_name in AssetCallbacks::slot_names() { + assert!( + faucet.storage().get(slot_name).is_some(), + "faucet should install the {slot_name} callback slot" + ); + } + assert_eq!(faucet.id().asset_callback_flag(), AssetCallbackFlag::Enabled); + } + /// Both agglayer network accounts allowlist the canonical [`ExpirationTransactionScript`], /// which the network transaction builder attaches to every network transaction. #[test] diff --git a/crates/miden-protocol/asm/kernels/transaction-core/src/prologue.masm b/crates/miden-protocol/asm/kernels/transaction-core/src/prologue.masm index f34c2f0824..68578fbd76 100644 --- a/crates/miden-protocol/asm/kernels/transaction-core/src/prologue.masm +++ b/crates/miden-protocol/asm/kernels/transaction-core/src/prologue.masm @@ -10,6 +10,8 @@ use miden::tx_kernel_core::asset_vault use miden::tx_kernel_core::asset use {ASSET_SIZE} from miden::tx_kernel_core::asset use {ACCOUNT_VERSION_1} from miden::tx_kernel_core::account +use {ON_BEFORE_ASSET_ADDED_TO_ACCOUNT_PROC_ROOT_SLOT, ON_BEFORE_ASSET_ADDED_TO_NOTE_PROC_ROOT_SLOT} + from miden::tx_kernel_core::callbacks use {EMPTY_SMT_ROOT, MAX_ASSETS_PER_NOTE, MAX_INPUT_NOTES_PER_TX, MAX_NOTE_STORAGE_ITEMS, NOTE_TREE_DEPTH} from miden::tx_kernel_core::constants use miden::tx_kernel_core::memory @@ -67,6 +69,9 @@ const ERR_PROLOGUE_NEW_ACCOUNT_NONCE_MUST_BE_ZERO = "new account must have a zer const ERR_PROLOGUE_NEW_ACCOUNT_UNSUPPORTED_VERSION = "new account metadata has an unsupported version" +const ERR_PROLOGUE_CALLBACK_SLOT_REQUIRES_ENABLED_ASSET_CALLBACK_FLAG = + "an account whose storage contains an asset callback slot must have the asset callback flag enabled" + const ERR_PROLOGUE_NUMBER_OF_NOTE_STORAGE_ITEMS_EXCEEDED_LIMIT = "number of note storage items exceeded the maximum limit of 1024" @@ -307,6 +312,7 @@ end #! - assert that the account nonce is set to 0. #! - read the account seed from the advice provider and assert it satisfies seed requirements. #! - assert that the storage slots and the account procedures are sorted and unique. +#! - assert that the asset callback flag is enabled if an asset callback slot is present. #! #! Validating storage and procedures for new accounts is sufficient because the storage and code #! commitments of an existing account are bound to its committed state, which was produced by the @@ -357,6 +363,51 @@ proc validate_new_account # --------------------------------------------------------------------------------------------- exec.account::validate_procedures # => [] + + # Assert the asset callback flag is consistent with the installed asset callback slots. + # --------------------------------------------------------------------------------------------- + exec.validate_asset_callbacks + # => [] +end + +#! Validates that an account whose storage contains an asset callback slot has the asset callback +#! flag of its account ID enabled. +#! +#! The kernel decides whether to invoke an account's asset callbacks solely from the asset callback +#! flag encoded in its account ID, and that flag is immutable once the ID is ground. A callback slot +#! installed on an account whose flag is disabled would therefore look correctly configured while +#! never being invoked, silently and permanently disabling whatever the callback enforces. +#! +#! `has_callback_slot` must imply `has_callbacks`, but not vice versa. That is, the callback flag +#! can be enabled without callback slots present. This is allowed so that an account retains the +#! ability to add a callback slot via an account upgrade later, which is particularly useful if new +#! types of callbacks are introduced. +#! +#! Inputs: [] +#! Outputs: [] +#! +#! Panics if: +#! - the account's storage contains an asset callback slot but its asset callback flag is disabled. +proc validate_asset_callbacks + push.ON_BEFORE_ASSET_ADDED_TO_ACCOUNT_PROC_ROOT_SLOT[0..2] exec.account::has_storage_slot + # => [has_account_callback_slot] + + push.ON_BEFORE_ASSET_ADDED_TO_NOTE_PROC_ROOT_SLOT[0..2] exec.account::has_storage_slot + # => [has_note_callback_slot, has_account_callback_slot] + + or + # => [has_callback_slot] + + exec.memory::get_native_account_id drop exec.account_id::asset_callback_flag + # => [has_callbacks, has_callback_slot] + + # the flag must be enabled if a callback slot is present, so reject the case where a slot is + # present but the flag is disabled + not and + # => [is_inconsistent] + + assertz.err=ERR_PROLOGUE_CALLBACK_SLOT_REQUIRES_ENABLED_ASSET_CALLBACK_FLAG + # => [] end #! Saves the account data to memory and validates it. diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/api.masm b/crates/miden-protocol/asm/kernels/transaction/lib/api.masm index d16d3c62cf..6502d4d91d 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/api.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/api.masm @@ -224,6 +224,11 @@ end pub proc account_upgrade # TODO(code_upgrades): Account upgrades must ensure the same conditions hold for an upgraded # account as validated in account::{validate_storage, validate_procedures}. + # The same applies to the asset callback rule validated in + # prologue::validate_asset_callbacks: an upgrade must reject adding an asset callback slot to an + # account whose asset callback flag is disabled. Such a callback is not invalid, it simply could + # never be invoked, since the flag is immutable, which leaves the account looking configured + # while nothing is enforced - the state the creation-time check rejects. # check that this procedure was executed against the native account exec.memory::assert_native_account # => [CODE_UPGRADE_COMMITMENT, STORAGE_UPGRADE_COMMITMENT, pad(8)] diff --git a/crates/miden-protocol/src/account/account_id/asset_callback_flag.rs b/crates/miden-protocol/src/account/account_id/asset_callback_flag.rs index 0caf550a65..0185a5546b 100644 --- a/crates/miden-protocol/src/account/account_id/asset_callback_flag.rs +++ b/crates/miden-protocol/src/account/account_id/asset_callback_flag.rs @@ -12,6 +12,11 @@ /// When [`Enabled`](Self::Enabled), the kernel dispatches the faucet's callbacks whenever one of /// its assets is added to a vault or note. When [`Disabled`](Self::Disabled), callbacks are skipped /// entirely and no foreign-account read is performed. +/// +/// The flag only enables the dispatch. On dispatch, the kernel reads the callback's procedure root +/// from the faucet's storage and skips the invocation if the callback slot is absent or holds the +/// empty word, so [`Enabled`](Self::Enabled) means callbacks may be invoked for the faucet's +/// assets, not that the faucet has any. #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[repr(u8)] pub enum AssetCallbackFlag { diff --git a/crates/miden-protocol/src/account/builder/mod.rs b/crates/miden-protocol/src/account/builder/mod.rs index 467933b6f6..31bdaa5597 100644 --- a/crates/miden-protocol/src/account/builder/mod.rs +++ b/crates/miden-protocol/src/account/builder/mod.rs @@ -13,7 +13,7 @@ use crate::account::{ AccountType, AssetCallbackFlag, }; -use crate::asset::{AssetCallbacks, AssetVault}; +use crate::asset::AssetVault; use crate::errors::AccountError; use crate::{Felt, Word}; @@ -29,6 +29,25 @@ use crate::{Felt, Word}; /// - The `account_type` set to [`AccountType::Private`]. /// - The `version` set to [`AccountIdVersion::Version1`]. /// +/// **Asset Callbacks** +/// +/// The [`AssetCallbackFlag`] determines whether the tx kernel dispatches asset callbacks for assets +/// issued by the account (if any) and is encoded into the resulting [`AccountId`] at creation. Note +/// that the flag only enables the dispatch: whether a callback actually runs additionally depends +/// on the corresponding callback slot being present and holding a non-empty procedure root, so an +/// enabled flag means callbacks may be invoked, not that the account has any. +/// +/// The flag is derived from the account's storage: it is [`AssetCallbackFlag::Enabled`] if any +/// component installs one of the protocol-reserved asset callback slots (see +/// [`AccountStorage::has_callback_slots`]) and [`AssetCallbackFlag::Disabled`] otherwise. There is +/// deliberately no way to disable the flag for an account that does install such a slot, since the +/// tx kernel gates callback invocation on the flag alone and the flag cannot be changed after the +/// ID is ground, so such a callback could never be invoked. +/// +/// The converse is allowed: [`AccountBuilder::enable_asset_callbacks`] enables the flag without +/// installing a callback slot, so that the account retains the ability to add a callback slot via +/// an account upgrade later. This is particularly useful if new types of callbacks are introduced. +/// /// [`AccountBuilder::with_component`] (or [`AccountBuilder::with_components`]) must be called at /// least once, and exactly one of the added components must be an authentication component (i.e. a /// component exporting a procedure marked with the `@auth_script` attribute). The auth component is @@ -101,14 +120,12 @@ impl AccountBuilder { self } - /// Sets the immutable [`AssetCallbackFlag`] of the account. + /// Enables the immutable [`AssetCallbackFlag`] of the account even if none of its components + /// install an asset callback slot. /// - /// This determines whether assets issued by the account (if any) trigger callbacks. It must be - /// set to [`AssetCallbackFlag::Enabled`] for faucets that configure a transfer policy, and - /// is encoded into the resulting [`AccountId`] at creation. Defaults to - /// [`AssetCallbackFlag::Disabled`]. - pub fn with_asset_callbacks(mut self, asset_callbacks: AssetCallbackFlag) -> Self { - self.asset_callbacks = asset_callbacks; + /// See the [type-level docs](AccountBuilder#asset-callbacks) for details. + pub fn enable_asset_callbacks(mut self) -> Self { + self.asset_callbacks = AssetCallbackFlag::Enabled; self } @@ -169,43 +186,15 @@ impl AccountBuilder { ) })?; - self.validate_asset_callbacks(&storage)?; - Ok((vault, code, storage)) } - /// Validates that the configured [`AssetCallbackFlag`] is consistent with the asset callback - /// slots installed by the builder's components. - /// - /// The kernel decides whether to invoke a faucet's asset callbacks solely from the - /// [`AssetCallbackFlag`] encoded in its [`AccountId`], and that flag is immutable once the ID - /// is ground. A component that installs a callback slot while the flag is - /// [`AssetCallbackFlag::Disabled`] therefore looks correctly configured but can never have its - /// callbacks invoked, silently and permanently disabling whatever the callbacks enforce. This - /// is rejected at build time so the misconfiguration cannot reach a deployed account. + /// Derives the account's [`AssetCallbackFlag`] from the asset callback slots installed by its + /// components. /// - /// The converse (the flag enabled without callback slots) is valid: the kernel skips the - /// callback when the slot is absent or holds the empty word. - fn validate_asset_callbacks(&self, storage: &AccountStorage) -> Result<(), AccountError> { - if self.asset_callbacks == AssetCallbackFlag::Enabled { - return Ok(()); - } - - for slot_name in [ - AssetCallbacks::on_before_asset_added_to_account_slot(), - AssetCallbacks::on_before_asset_added_to_note_slot(), - ] { - if storage.get(slot_name).is_some_and(|slot| !slot.value().is_empty()) { - return Err(AccountError::BuildError( - format!( - "component installs the asset callback slot `{slot_name}` but the account's asset callback flag is disabled, so the callback would never be invoked" - ), - None, - )); - } - } - - Ok(()) + /// See the [type-level docs](AccountBuilder#asset-callbacks) for details. + fn derive_asset_callbacks(&self, storage: &AccountStorage) -> AssetCallbackFlag { + AssetCallbackFlag::from(self.asset_callbacks.is_enabled() || storage.has_callback_slots()) } /// Grinds a new [`AccountId`] using the `init_seed` as a starting point. @@ -213,13 +202,14 @@ impl AccountBuilder { &self, init_seed: [u8; 32], version: AccountIdVersion, + asset_callbacks: AssetCallbackFlag, code_commitment: Word, storage_commitment: Word, ) -> Result { let seed = AccountIdV1::compute_account_seed( init_seed, self.account_type, - self.asset_callbacks, + asset_callbacks, version, code_commitment, storage_commitment, @@ -245,8 +235,6 @@ impl AccountBuilder { /// - The number of [`StorageSlot`](crate::account::StorageSlot)s of all components exceeds 255. /// - [`MastForest::merge`](miden_processor::mast::MastForest::merge) fails on the given /// components. - /// - A component installs an asset callback slot while the configured [`AssetCallbackFlag`] is - /// [`AssetCallbackFlag::Disabled`], since the kernel would never invoke that callback. /// - If duplicate assets were added to the builder (only under the `testing` feature). /// - If the vault is not empty on new accounts (only under the `testing` feature). pub fn build(mut self) -> Result { @@ -260,9 +248,12 @@ impl AccountBuilder { )); } + let asset_callbacks = self.derive_asset_callbacks(&storage); + let seed = self.grind_account_id( self.init_seed, self.id_version, + asset_callbacks, code.commitment(), storage.to_commitment(), )?; @@ -276,7 +267,7 @@ impl AccountBuilder { .expect("get_account_seed should provide a suitable seed"); debug_assert_eq!(account_id.account_type(), self.account_type); - debug_assert_eq!(account_id.asset_callback_flag(), self.asset_callbacks); + debug_assert_eq!(account_id.asset_callback_flag(), asset_callbacks); // SAFETY: The account ID was derived from the seed and the seed is provided, so it is safe // to bypass the checks of `Account::new`. @@ -322,7 +313,7 @@ impl AccountBuilder { bytes, AccountIdVersion::Version1, self.account_type, - self.asset_callbacks, + self.derive_asset_callbacks(&storage), ) }; @@ -347,6 +338,7 @@ mod tests { use super::*; use crate::account::component::AccountComponentMetadata; use crate::account::{AccountProcedureRoot, StorageSlot, StorageSlotName}; + use crate::asset::AssetCallbacks; use crate::testing::assembler::assemble_test_package; use crate::testing::noop_auth_component::NoopAuthComponent; @@ -596,12 +588,12 @@ mod tests { assert_matches!(build_error, AccountError::BuildError(msg, _) if msg == "account asset vault must be empty on new accounts") } - /// A component that installs an asset callback slot must not be built into an account whose - /// [`AssetCallbackFlag`] is disabled: the kernel gates callback invocation on that flag alone - /// and the flag is immutable once the ID is ground, so whatever the callback enforces would - /// be silently and permanently bypassed. + /// The [`AssetCallbackFlag`] is derived from the installed asset callback slots: the kernel + /// gates callback invocation on that flag alone and the flag is immutable once the ID is + /// ground, so an account that installs a callback slot must have callbacks enabled or whatever + /// the callback enforces would be silently and permanently bypassed. #[test] - fn account_builder_rejects_callback_slot_with_disabled_flag() { + fn account_builder_derives_asset_callback_flag_from_callback_slots() { let callback_component = |slots| { AccountComponent::new( CUSTOM_PACKAGE1.clone(), @@ -619,23 +611,30 @@ mod tests { .on_before_asset_added_to_account(Word::from([1u32, 2, 3, 4])) .into_storage_slots(), ] { - let build_error = Account::builder([7; 32]) - .with_component(NoopAuthComponent) - .with_component(callback_component(slots.clone())) - .build() - .unwrap_err(); - - assert_matches!(build_error, AccountError::BuildError(msg, _) if msg.contains("asset callback flag is disabled")); - - // The same component is accepted once the flag is enabled. - Account::builder([7; 32]) - .with_asset_callbacks(AssetCallbackFlag::Enabled) + let account = Account::builder([7; 32]) .with_component(NoopAuthComponent) .with_component(callback_component(slots)) .build() .unwrap(); + + assert_eq!(account.id().asset_callback_flag(), AssetCallbackFlag::Enabled); } } + /// Without an installed callback slot the flag is disabled, unless callbacks are explicitly + /// enabled to reserve the capability for the account's lifetime. + #[test] + fn account_builder_derives_disabled_asset_callback_flag_without_callback_slots() { + let builder = Account::builder([7; 32]) + .with_component(NoopAuthComponent) + .with_component(CustomComponent1 { slot0: 25 }); + + let account = builder.clone().build().unwrap(); + assert_eq!(account.id().asset_callback_flag(), AssetCallbackFlag::Disabled); + + let account = builder.enable_asset_callbacks().build().unwrap(); + assert_eq!(account.id().asset_callback_flag(), AssetCallbackFlag::Enabled); + } + // TODO: Test that a BlockHeader with a number which is not a multiple of 2^16 returns an error. } diff --git a/crates/miden-protocol/src/account/mod.rs b/crates/miden-protocol/src/account/mod.rs index ddb0a92af3..cf3ded8ac1 100644 --- a/crates/miden-protocol/src/account/mod.rs +++ b/crates/miden-protocol/src/account/mod.rs @@ -132,6 +132,8 @@ impl Account { /// - an account seed is not provided but the account's nonce indicates the account is new. /// - an account seed is provided but the account ID derived from it is invalid or does not /// match the provided account's ID. + /// - the storage contains an asset callback slot while the account ID's [`AssetCallbackFlag`] + /// is [`AssetCallbackFlag::Disabled`]. pub fn new( id: AccountId, vault: AssetVault, @@ -141,6 +143,7 @@ impl Account { seed: Option, ) -> Result { validate_account_seed(id, code.commitment(), storage.to_commitment(), seed, nonce)?; + validate_asset_callbacks(id, &storage)?; Ok(Self::new_unchecked(id, vault, storage, code, nonce, seed)) } @@ -547,6 +550,22 @@ impl Deserializable for Account { // HELPER FUNCTIONS // ================================================================================================ +/// Validates that an account which installs an asset callback slot has callbacks enabled. +/// +/// The transaction kernel rejects such accounts when they are created; this mirrors that rule for +/// accounts that are constructed or deserialized outside of a transaction. See the +/// [`AccountBuilder`](AccountBuilder#asset-callbacks) docs for details. +pub(super) fn validate_asset_callbacks( + id: AccountId, + storage: &AccountStorage, +) -> Result<(), AccountError> { + if !id.asset_callback_flag().is_enabled() && storage.has_callback_slots() { + return Err(AccountError::AssetCallbackSlotWithDisabledFlag(id)); + } + + Ok(()) +} + /// Validates that the provided seed is valid for the provided account components. pub(super) fn validate_account_seed( id: AccountId, @@ -606,7 +625,7 @@ mod tests { StorageSlotContent, StorageSlotName, }; - use crate::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset}; + use crate::asset::{Asset, AssetCallbacks, AssetVault, FungibleAsset, NonFungibleAsset}; use crate::errors::AccountError; use crate::testing::account_id::{ ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, @@ -854,6 +873,31 @@ mod tests { Account::new_existing(id, vault, storage, code, nonce) } + /// Accounts constructed outside of the builder are rejected if they install a callback slot + /// without having callbacks enabled. + #[test] + fn account_new_rejects_callback_slot_with_disabled_flag() -> anyhow::Result<()> { + let account = AccountBuilder::new([5; 32]) + .with_component(NoopAuthComponent) + .with_component(AddComponent) + .build_existing()?; + assert_eq!(account.id().asset_callback_flag(), AssetCallbackFlag::Disabled); + + let (id, vault, storage, code, nonce, _seed) = account.into_parts(); + + let mut slots = storage.into_slots(); + slots.push(StorageSlot::with_value( + AssetCallbacks::on_before_asset_added_to_account_slot().clone(), + Word::from([1u32, 2, 3, 4]), + )); + let storage = AccountStorage::new(slots)?; + + let err = Account::new(id, vault, storage, code, nonce, None).unwrap_err(); + assert_matches!(err, AccountError::AssetCallbackSlotWithDisabledFlag(_)); + + Ok(()) + } + /// Tests all cases of account ID seed validation. #[test] fn seed_validation() -> anyhow::Result<()> { diff --git a/crates/miden-protocol/src/account/storage/mod.rs b/crates/miden-protocol/src/account/storage/mod.rs index 900838f057..d4fd48c947 100644 --- a/crates/miden-protocol/src/account/storage/mod.rs +++ b/crates/miden-protocol/src/account/storage/mod.rs @@ -19,6 +19,7 @@ use crate::account::{ StorageSlotPatch, StorageValuePatch, }; +use crate::asset::AssetCallbacks; use crate::crypto::SequentialCommit; pub(crate) mod slot; @@ -164,6 +165,20 @@ impl AccountStorage { self.slots.iter().find(|slot| slot.name().id() == slot_name.id()) } + /// Returns `true` if the storage contains at least one of the protocol-reserved asset callback + /// slots, `false` otherwise. + /// + /// Only the presence of a callback slot is relevant, not its value: a slot's value can be + /// rewritten over the account's lifetime, while its presence can only change through an account + /// upgrade, so only the presence can be tied to the immutable + /// [`AssetCallbackFlag`](crate::account::AssetCallbackFlag) encoded in the account ID. See the + /// [`AccountBuilder`](crate::account::AccountBuilder#asset-callbacks) docs for details. + pub fn has_callback_slots(&self) -> bool { + AssetCallbacks::slot_names() + .iter() + .any(|slot_name| self.get(slot_name).is_some()) + } + /// Returns a mutable reference to the storage slot with the provided name, if it exists, `None` /// otherwise. fn get_mut(&mut self, slot_name: &StorageSlotName) -> Option<&mut StorageSlot> { diff --git a/crates/miden-protocol/src/asset/asset_callbacks.rs b/crates/miden-protocol/src/asset/asset_callbacks.rs index 3edf662f2f..ee07ba2b9c 100644 --- a/crates/miden-protocol/src/asset/asset_callbacks.rs +++ b/crates/miden-protocol/src/asset/asset_callbacks.rs @@ -73,6 +73,14 @@ impl AssetCallbacks { &ON_BEFORE_ASSET_ADDED_TO_NOTE_SLOT_NAME } + /// Returns the names of all protocol-reserved asset callback storage slots. + pub fn slot_names() -> [&'static StorageSlotName; 2] { + [ + Self::on_before_asset_added_to_account_slot(), + Self::on_before_asset_added_to_note_slot(), + ] + } + /// Returns the procedure root of the `on_before_asset_added_to_account` callback. pub fn on_before_asset_added_to_account_proc_root(&self) -> Word { self.on_before_asset_added_to_account diff --git a/crates/miden-protocol/src/errors/mod.rs b/crates/miden-protocol/src/errors/mod.rs index 3d61c74a6f..f42675a2e6 100644 --- a/crates/miden-protocol/src/errors/mod.rs +++ b/crates/miden-protocol/src/errors/mod.rs @@ -136,6 +136,10 @@ pub enum AccountError { AccountComponentMastForestMergeError(#[source] MastForestError), #[error("account component contains multiple authentication procedures")] AccountComponentMultipleAuthProcedures, + #[error( + "storage of account {0} contains an asset callback slot but its asset callback flag is disabled, so the callback would never be invoked" + )] + AssetCallbackSlotWithDisabledFlag(AccountId), #[error("failed to update asset vault")] AssetVaultUpdateError(#[source] AssetVaultError), #[error("account build error: {0}")] diff --git a/crates/miden-standards/src/account/faucets/fungible/mod.rs b/crates/miden-standards/src/account/faucets/fungible/mod.rs index 9815a712e9..17b65e7617 100644 --- a/crates/miden-standards/src/account/faucets/fungible/mod.rs +++ b/crates/miden-standards/src/account/faucets/fungible/mod.rs @@ -18,7 +18,6 @@ use miden_protocol::account::{ AccountProcedureRoot, AccountStorage, AccountType, - AssetCallbackFlag, StorageSlot, StorageSlotName, }; @@ -575,10 +574,8 @@ pub fn create_singlesig_user_fungible_faucet( token_policy_manager: TokenPolicyManager, account_type: AccountType, ) -> Result { - let asset_callbacks = AssetCallbackFlag::from(token_policy_manager.has_transfer_policy()); AccountBuilder::new(init_seed) .account_type(account_type) - .with_asset_callbacks(asset_callbacks) .with_component(auth_component) .with_component(faucet) .with_component(Authority::AuthControlled) @@ -597,10 +594,8 @@ pub fn create_multisig_user_fungible_faucet( token_policy_manager: TokenPolicyManager, account_type: AccountType, ) -> Result { - let asset_callbacks = AssetCallbackFlag::from(token_policy_manager.has_transfer_policy()); AccountBuilder::new(init_seed) .account_type(account_type) - .with_asset_callbacks(asset_callbacks) .with_component(auth_component) .with_component(faucet) .with_component(Authority::AuthControlled) @@ -619,10 +614,8 @@ pub fn create_guarded_user_fungible_faucet( token_policy_manager: TokenPolicyManager, account_type: AccountType, ) -> Result { - let asset_callbacks = AssetCallbackFlag::from(token_policy_manager.has_transfer_policy()); AccountBuilder::new(init_seed) .account_type(account_type) - .with_asset_callbacks(asset_callbacks) .with_component(auth_component) .with_component(faucet) .with_component(Authority::AuthControlled) @@ -648,11 +641,8 @@ pub fn create_network_fungible_faucet( fee_policy_manager: FeePolicyManager, ) -> Result { let note_allowlist = [MintNote::script_root(), BurnNote::script_root()].into_iter().collect(); - let asset_callbacks = AssetCallbackFlag::from(token_policy_manager.has_transfer_policy()); - NetworkAccount::builder(init_seed, note_allowlist, fee_policy_manager) .expect("MintNote + BurnNote allowlist is non-empty") - .with_asset_callbacks(asset_callbacks) .with_component(faucet) .with_components(access_control) .with_components(token_policy_manager) diff --git a/crates/miden-standards/src/account/faucets/non_fungible/mod.rs b/crates/miden-standards/src/account/faucets/non_fungible/mod.rs index f2af4c1c9d..2f603bb8cf 100644 --- a/crates/miden-standards/src/account/faucets/non_fungible/mod.rs +++ b/crates/miden-standards/src/account/faucets/non_fungible/mod.rs @@ -17,7 +17,6 @@ use miden_protocol::account::{ AccountProcedureRoot, AccountStorage, AccountType, - AssetCallbackFlag, StorageMap, StorageMapKey, StorageSlot, @@ -520,10 +519,8 @@ pub fn create_user_non_fungible_faucet( token_policy_manager: TokenPolicyManager, account_type: AccountType, ) -> Result { - let asset_callbacks = AssetCallbackFlag::from(token_policy_manager.has_transfer_policy()); AccountBuilder::new(init_seed) .account_type(account_type) - .with_asset_callbacks(asset_callbacks) .with_component(auth_component) .with_component(faucet) .with_component(Authority::AuthControlled) @@ -551,11 +548,8 @@ pub fn create_network_non_fungible_faucet( fee_policy_manager: FeePolicyManager, ) -> Result { let note_allowlist = [MintNote::script_root(), BurnNote::script_root()].into_iter().collect(); - let asset_callbacks = AssetCallbackFlag::from(token_policy_manager.has_transfer_policy()); - NetworkAccount::builder(init_seed, note_allowlist, fee_policy_manager) .expect("MintNote + BurnNote allowlist is non-empty") - .with_asset_callbacks(asset_callbacks) .with_component(faucet) .with_components(access_control) .with_components(token_policy_manager) diff --git a/crates/miden-standards/src/account/policies/manager.rs b/crates/miden-standards/src/account/policies/manager.rs index 368e1bdb4a..5b6b5d6a17 100644 --- a/crates/miden-standards/src/account/policies/manager.rs +++ b/crates/miden-standards/src/account/policies/manager.rs @@ -206,16 +206,24 @@ struct PolicyConfig { /// ([`TokenPolicyManagerBuilder::allowed_send_policy`] / /// [`TokenPolicyManagerBuilder::allowed_receive_policy`]) for runtime switching. The /// protocol-reserved asset-callback slots (see the storage layout below) are installed whenever at -/// least one send or receive policy of either kind is registered - active or reserved - so the -/// faucet's account ID must be created with -/// [`AssetCallbackFlag::Enabled`][miden_protocol::account::AssetCallbackFlag::Enabled] -/// (see [`Self::has_transfer_policy`]), even when only reserved policies exist and no active root -/// is set yet. Because this flag is an immutable property of the account ID, it applies for the -/// faucet's entire lifetime, so promoting a reserved policy later via -/// `set_send_policy` / `set_receive_policy` enforces it against the whole circulating supply rather -/// than only assets minted after the switch. The slots are omitted only when no send or receive -/// policy of any kind is registered, in which case the faucet's account ID is created with -/// [`AssetCallbackFlag::Disabled`][miden_protocol::account::AssetCallbackFlag::Disabled]. +/// least one send or receive policy of either kind is registered - active or reserved - even when +/// only reserved policies exist and no active root is set yet (see [`Self::has_transfer_policy`]). +/// Installing those slots is what makes the faucet's account ID carry +/// [`AssetCallbackFlag::Enabled`][miden_protocol::account::AssetCallbackFlag::Enabled]. Because +/// the flag is an immutable property of the account ID, it applies for the faucet's entire +/// lifetime, so promoting a reserved policy later via `set_send_policy` / `set_receive_policy` +/// enforces it against the whole circulating supply rather than only assets minted after the +/// switch. +/// +/// The slots are omitted only when no send or receive policy of any kind is registered, in which +/// case the faucet's account ID is created with +/// [`AssetCallbackFlag::Disabled`][miden_protocol::account::AssetCallbackFlag::Disabled] and no +/// transfer policy can ever be enforced for that faucet. Such a faucet also avoids the cost the +/// flag imposes: with callbacks enabled, the tx kernel starts a foreign context against the faucet +/// on every movement of its assets, so every holder's transaction must supply the faucet's account +/// state. A faucet that wants to keep the option of a transfer policy open without registering one +/// yet can enable the flag explicitly with +/// [`AccountBuilder::enable_asset_callbacks`][miden_protocol::account::AccountBuilder::enable_asset_callbacks]. /// /// ## Storage layout /// @@ -230,8 +238,8 @@ struct PolicyConfig { /// - Asset-callback storage slots (registered via [`AssetCallbacks`]) hold the fixed /// `invoke_send_policy` / `invoke_receive_policy` wrapper roots, so the kernel dispatches to the /// wrapper (which then dispatches to the active policy in the slot above). They are installed -/// only when at least one transfer policy is configured, so a faucet with this manager must be -/// created with its account ID's +/// only when at least one transfer policy is configured, which is also what makes the faucet's +/// account ID carry /// [`AssetCallbackFlag::Enabled`][miden_protocol::account::AssetCallbackFlag::Enabled], and /// future policy switches via `set_send_policy` / `set_receive_policy` apply to the entire /// circulating supply rather than only to assets minted after the switch. @@ -585,7 +593,7 @@ impl TokenPolicyManager { } /// Returns `true` if at least one send or receive policy is configured, in which case the - /// faucet registers the protocol callback slots and its account ID must be created with + /// faucet registers the protocol callback slots and therefore its account ID is created with /// [`AssetCallbackFlag::Enabled`](miden_protocol::account::AssetCallbackFlag::Enabled). pub fn has_transfer_policy(&self) -> bool { self.policies.iter().any(|(_, cfg)| { @@ -635,6 +643,14 @@ impl TokenPolicyManager { // and then dispatches to whatever active root lives in the `active_*_policy` slot above. // This indirection lets `set_send_policy` / `set_receive_policy` switch the active policy // for the entire circulating supply without touching the callback slots. + // + // The slots are what make the account ID carry an enabled asset callback flag, and that + // flag is immutable, so a faucet created without a transfer policy can never enforce one + // later. That is deliberate: an enabled flag makes the tx kernel start a foreign context + // against the faucet on every movement of its assets, so every holder's transaction has to + // supply the faucet's account state. A faucet that wants to keep the option open without + // registering a policy yet can enable the flag explicitly with + // `AccountBuilder::enable_asset_callbacks`. if self.has_transfer_policy() { let callback_slots = AssetCallbacks::new() .on_before_asset_added_to_account(Self::invoke_receive_policy_root().as_word()) diff --git a/crates/miden-testing/src/kernel_tests/tx/test_callbacks.rs b/crates/miden-testing/src/kernel_tests/tx/test_callbacks.rs index 95b65d0dc5..0efc421786 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_callbacks.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_callbacks.rs @@ -11,6 +11,7 @@ use miden_protocol::account::{ AccountComponent, AccountComponentCode, AccountId, + AccountIdVersion, AccountProcedureRoot, AccountType, AssetCallbackFlag, @@ -24,13 +25,17 @@ use miden_protocol::asset::{ AssetAmount, AssetCallbacks, AssetComposition, + AssetVault, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails, }; use miden_protocol::block::account_tree::AccountIdKey; use miden_protocol::errors::MasmError; -use miden_protocol::errors::tx_kernel::ERR_FAUCET_CALLBACK_PROC_ROOT_NOT_PART_OF_ACCOUNT_CODE; +use miden_protocol::errors::tx_kernel::{ + ERR_FAUCET_CALLBACK_PROC_ROOT_NOT_PART_OF_ACCOUNT_CODE, + ERR_PROLOGUE_CALLBACK_SLOT_REQUIRES_ENABLED_ASSET_CALLBACK_FLAG, +}; use miden_protocol::note::{NoteTag, NoteType}; use miden_protocol::utils::sync::LazyLock; use miden_protocol::{Felt, Word}; @@ -244,15 +249,21 @@ async fn test_faucet_without_callback_slot_skips_callback( let target_account = builder.add_existing_wallet(Auth::IncrNonce)?; // Create a faucet whose ID enables callbacks, but WITHOUT a populated AssetCallbacks slot. + // The flag has to be enabled explicitly here, since without a callback slot the builder would + // otherwise derive it as disabled. let mut account_builder = AccountBuilder::new([45u8; 32]) .account_type(AccountType::Public) - .with_asset_callbacks(AssetCallbackFlag::Enabled) + .enable_asset_callbacks() .with_component(MockFaucetComponent); - // If callback proc roots should be empty, add the empty storage slots. + // If callback proc roots should be empty, add the callback slots holding the empty word. + // `AssetCallbacks` cannot express these, since it only emits slots for non-empty roots. if has_empty_callback_proc_root { let name = "miden::testing::callbacks"; - let slots = AssetCallbacks::new().into_storage_slots(); + let slots = AssetCallbacks::slot_names() + .into_iter() + .map(|slot_name| StorageSlot::with_value(slot_name.clone(), Word::empty())) + .collect(); let component = AccountComponent::new( CodeBuilder::new().compile_component_code(name, "pub proc dummy nop end")?, slots, @@ -323,7 +334,6 @@ async fn test_faucet_with_invalid_callback_root_fails() -> anyhow::Result<()> { let account_builder = AccountBuilder::new([45u8; 32]) .account_type(AccountType::Public) - .with_asset_callbacks(AssetCallbackFlag::Enabled) .with_component(MockFaucetComponent) .with_component(component); @@ -748,6 +758,61 @@ async fn test_faucet_with_callback_calls_itself() -> anyhow::Result<()> { Ok(()) } +/// Tests that a new account whose storage contains an asset callback slot is rejected if its +/// account ID has the asset callback flag disabled. +#[tokio::test] +async fn test_new_account_with_callback_slot_and_disabled_flag_fails() -> anyhow::Result<()> { + let mut mock_chain = MockChain::new(); + mock_chain.prove_next_block()?; + + // The block list component installs both callback slots, so the builder derives an enabled + // flag for this account. + let faucet = AccountBuilder::new([45u8; 32]) + .account_type(AccountType::Public) + .with_components(Auth::IncrNonce) + .with_component(MockFaucetComponent) + .with_component(BlockList::new(BTreeSet::new())) + .build()?; + assert_eq!(faucet.id().asset_callback_flag(), AssetCallbackFlag::Enabled); + + // Re-grind the ID of the very same code and storage, but with callbacks disabled. + let seed = AccountId::compute_account_seed( + [45; 32], + AccountType::Public, + AssetCallbackFlag::Disabled, + AccountIdVersion::Version1, + faucet.code().commitment(), + faucet.storage().to_commitment(), + )?; + let account_id = AccountId::new( + seed, + AccountIdVersion::Version1, + faucet.code().commitment(), + faucet.storage().to_commitment(), + )?; + assert_eq!(account_id.asset_callback_flag(), AssetCallbackFlag::Disabled); + + let account = Account::new_unchecked( + account_id, + AssetVault::default(), + faucet.storage().clone(), + faucet.code().clone(), + Felt::ZERO, + Some(seed), + ); + + let mock_tx = mock_chain.build_transaction(account).build()?; + + let result = mock_tx.execute().await; + + assert_transaction_executor_error!( + result, + ERR_PROLOGUE_CALLBACK_SLOT_REQUIRES_ENABLED_ASSET_CALLBACK_FLAG + ); + + Ok(()) +} + // HELPERS // ================================================================================================ @@ -764,7 +829,6 @@ fn add_faucet_with_block_list( let account_builder = AccountBuilder::new([42u8; 32]) .account_type(AccountType::Public) - .with_asset_callbacks(AssetCallbackFlag::Enabled) .with_component(MockFaucetComponent) .with_component(block_list); @@ -830,7 +894,6 @@ fn add_faucet_with_callbacks( let account_builder = AccountBuilder::new([42; 32]) .account_type(AccountType::Public) - .with_asset_callbacks(AssetCallbackFlag::Enabled) .with_component(faucet) .with_component(Authority::AuthControlled) .with_components( diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 7d584ad946..a9cab66d86 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -27,7 +27,6 @@ use miden_protocol::account::{ AccountPatch, AccountType, AccountUpdateDetails, - AssetCallbackFlag, StorageSlot, }; use miden_protocol::asset::{Asset, AssetAmount, FungibleAsset, TokenSymbol}; @@ -398,9 +397,6 @@ impl MockChainBuilder { .account_type(account_type) .with_component(faucet) .with_components(access_control) - .with_asset_callbacks(AssetCallbackFlag::from( - token_policy_manager.has_transfer_policy(), - )) .with_components(token_policy_manager) .with_component(Pausable::unpaused()) .with_component(PausableManager) @@ -456,7 +452,6 @@ impl MockChainBuilder { .account_type(AccountType::Public) .with_component(faucet) .with_component(Authority::AuthControlled) - .with_asset_callbacks(AssetCallbackFlag::Disabled) .with_components(token_policy_manager) .with_component(Pausable::unpaused()) .with_component(PausableManager); @@ -490,7 +485,6 @@ impl MockChainBuilder { .account_type(AccountType::Public) .with_component(faucet) .with_component(Authority::AuthControlled) - .with_asset_callbacks(AssetCallbackFlag::Enabled) .with_components(token_policy_manager) .with_component(Pausable::unpaused()); @@ -641,7 +635,6 @@ impl MockChainBuilder { .account_type(AccountType::Public) .with_component(faucet) .with_component(Authority::AuthControlled) - .with_asset_callbacks(AssetCallbackFlag::Disabled) .with_components(token_policy_manager) .with_component(Pausable::unpaused()) .with_component(PausableManager); diff --git a/crates/miden-testing/tests/agglayer/bridge_in.rs b/crates/miden-testing/tests/agglayer/bridge_in.rs index 8282331960..064085c7bd 100644 --- a/crates/miden-testing/tests/agglayer/bridge_in.rs +++ b/crates/miden-testing/tests/agglayer/bridge_in.rs @@ -25,7 +25,7 @@ use miden_agglayer::{ }; use miden_protocol::Felt; use miden_protocol::account::auth::AuthScheme; -use miden_protocol::account::{Account, AccountId, AccountType, AssetCallbackFlag}; +use miden_protocol::account::{Account, AccountId, AccountType}; use miden_protocol::asset::{Asset, AssetAmount, FungibleAsset}; use miden_protocol::crypto::SequentialCommit; use miden_protocol::crypto::rand::FeltRng; @@ -201,7 +201,6 @@ async fn test_bridge_in_claim_to_p2id( bridge_account.id(), verification_base_fee, )? - .with_asset_callbacks(AssetCallbackFlag::Enabled) .build_existing()?; builder.add_account(agglayer_faucet.clone())?; diff --git a/crates/miden-testing/tests/scripts/allowlist/mod.rs b/crates/miden-testing/tests/scripts/allowlist/mod.rs index c47b84f15f..761491c805 100644 --- a/crates/miden-testing/tests/scripts/allowlist/mod.rs +++ b/crates/miden-testing/tests/scripts/allowlist/mod.rs @@ -18,7 +18,6 @@ use miden_protocol::account::{ AccountId, AccountProcedureRoot, AccountType, - AssetCallbackFlag, RoleSymbol, }; use miden_protocol::asset::{Asset, AssetAmount, FungibleAsset}; @@ -87,7 +86,6 @@ fn add_faucet_with_owner_allowlist_transfer_initialized( let account_builder = AccountBuilder::new([43u8; 32]) .account_type(AccountType::Public) - .with_asset_callbacks(AssetCallbackFlag::Enabled) .with_component(faucet) .with_component(Ownable2Step::new(owner_id)) .with_component(Authority::OwnerControlled) @@ -567,7 +565,6 @@ fn add_rbac_faucet_with_allowlist( let account_builder = AccountBuilder::new([71u8; 32]) .account_type(AccountType::Public) - .with_asset_callbacks(AssetCallbackFlag::Enabled) .with_component(faucet) .with_components(AccessControl::Rbac { admin, diff --git a/crates/miden-testing/tests/scripts/blocklist/mod.rs b/crates/miden-testing/tests/scripts/blocklist/mod.rs index 5c384fd955..c7e946332b 100644 --- a/crates/miden-testing/tests/scripts/blocklist/mod.rs +++ b/crates/miden-testing/tests/scripts/blocklist/mod.rs @@ -18,7 +18,6 @@ use miden_protocol::account::{ AccountId, AccountProcedureRoot, AccountType, - AssetCallbackFlag, RoleSymbol, }; use miden_protocol::asset::{Asset, AssetAmount, FungibleAsset}; @@ -88,7 +87,6 @@ fn add_faucet_with_owner_blocklist_transfer_initialized( let account_builder = AccountBuilder::new([43u8; 32]) .account_type(AccountType::Public) - .with_asset_callbacks(AssetCallbackFlag::Enabled) .with_component(faucet) .with_component(Ownable2Step::new(owner_id)) .with_component(Authority::OwnerControlled) @@ -567,7 +565,6 @@ fn add_rbac_faucet_with_blocklist( let account_builder = AccountBuilder::new([71u8; 32]) .account_type(AccountType::Public) - .with_asset_callbacks(AssetCallbackFlag::Enabled) .with_component(faucet) .with_components(AccessControl::Rbac { admin, diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 409256546f..7663aacd58 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -11,7 +11,6 @@ use miden_protocol::account::{ AccountId, AccountProcedureRoot, AccountType, - AssetCallbackFlag, }; use miden_protocol::assembly::DefaultSourceManager; use miden_protocol::asset::{Asset, AssetAmount, FungibleAsset, NonFungibleAsset, TokenSymbol}; @@ -302,7 +301,6 @@ fn build_network_faucet_with_burn_switching( .with_component(faucet) .with_component(Ownable2Step::new(owner)) .with_component(Authority::OwnerControlled) - .with_asset_callbacks(AssetCallbackFlag::from(token_policy_manager.has_transfer_policy())) .with_components(token_policy_manager) .with_component(Pausable::unpaused()); @@ -340,7 +338,6 @@ fn build_existing_faucet_with_reserved_only_transfer_policy( .with_component(faucet) .with_component(Ownable2Step::new(owner)) .with_component(Authority::OwnerControlled) - .with_asset_callbacks(AssetCallbackFlag::from(token_policy_manager.has_transfer_policy())) .with_components(token_policy_manager); builder.add_account_from_builder(Auth::IncrNonce, account_builder, AccountState::Exists) @@ -383,7 +380,6 @@ fn build_network_faucet_with_min_burn_amount( .with_component(faucet) .with_component(Ownable2Step::new(owner)) .with_component(Authority::OwnerControlled) - .with_asset_callbacks(AssetCallbackFlag::from(token_policy_manager.has_transfer_policy())) .with_components(token_policy_manager) .with_component(Pausable::unpaused()); @@ -2346,7 +2342,6 @@ fn build_network_faucet_mutable_max_supply( .with_component(faucet) .with_component(Ownable2Step::new(owner)) .with_component(Authority::OwnerControlled) - .with_asset_callbacks(AssetCallbackFlag::from(token_policy_manager.has_transfer_policy())) .with_components(token_policy_manager) .with_component(Pausable::unpaused()); @@ -2689,7 +2684,6 @@ fn build_network_faucet_with_blocklist_transfer( .with_component(faucet) .with_component(Ownable2Step::new(owner)) .with_component(Authority::OwnerControlled) - .with_asset_callbacks(AssetCallbackFlag::from(token_policy_manager.has_transfer_policy())) .with_components(token_policy_manager) .with_component(Pausable::unpaused()); diff --git a/crates/miden-testing/tests/scripts/faucet_policy_config.rs b/crates/miden-testing/tests/scripts/faucet_policy_config.rs index 88216bc48b..c8aeb648b8 100644 --- a/crates/miden-testing/tests/scripts/faucet_policy_config.rs +++ b/crates/miden-testing/tests/scripts/faucet_policy_config.rs @@ -12,7 +12,7 @@ extern crate alloc; use alloc::vec::Vec; use miden_processor::crypto::random::RandomCoin; -use miden_protocol::account::{Account, AccountBuilder, AccountId, AccountType, AssetCallbackFlag}; +use miden_protocol::account::{Account, AccountBuilder, AccountId, AccountType}; use miden_protocol::asset::AssetAmount; use miden_protocol::errors::protocol::ERR_NOTE_TOO_MANY_STORAGE_ITEMS; use miden_protocol::note::Note; @@ -77,7 +77,6 @@ fn create_faucet_with_policies( .with_component(faucet) .with_component(Ownable2Step::new(owner)) .with_component(Authority::OwnerControlled) - .with_asset_callbacks(AssetCallbackFlag::from(token_policy_manager.has_transfer_policy())) .with_components(token_policy_manager); builder.add_account_from_builder(Auth::IncrNonce, account_builder, AccountState::Exists) diff --git a/crates/miden-testing/tests/scripts/min_burn_amount_config.rs b/crates/miden-testing/tests/scripts/min_burn_amount_config.rs index 6162e41534..de502f4a95 100644 --- a/crates/miden-testing/tests/scripts/min_burn_amount_config.rs +++ b/crates/miden-testing/tests/scripts/min_burn_amount_config.rs @@ -11,7 +11,7 @@ extern crate alloc; use alloc::vec::Vec; use miden_processor::crypto::random::RandomCoin; -use miden_protocol::account::{Account, AccountBuilder, AccountId, AccountType, AssetCallbackFlag}; +use miden_protocol::account::{Account, AccountBuilder, AccountId, AccountType}; use miden_protocol::asset::AssetAmount; use miden_protocol::errors::protocol::ERR_NOTE_TOO_MANY_STORAGE_ITEMS; use miden_protocol::note::Note; @@ -62,7 +62,6 @@ fn create_faucet_with_min_burn_amount(owner: AccountId) -> anyhow::Result anyhow:: let account_builder = AccountBuilder::new([44u8; 32]) .account_type(AccountType::Public) - .with_asset_callbacks(AssetCallbackFlag::Disabled) .with_component(faucet) .with_component(BasicWallet) .with_component(Authority::AuthControlled)