From 2a93cf1e8dc538b0bf27684325376d0291e9eda7 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 18 May 2026 16:43:18 +0200 Subject: [PATCH 01/12] merge AuthMethod into AccessControl --- .../src/account/access/authority.rs | 17 +- .../miden-standards/src/account/access/mod.rs | 111 ++++++--- .../src/account/faucets/fungible/mod.rs | 231 ++++++++++++++---- .../src/account/faucets/fungible/tests.rs | 189 ++++++++++---- .../src/account/faucets/mod.rs | 6 + .../src/account/policies/manager.rs | 67 +++++ .../src/mock_chain/chain_builder.rs | 48 ++-- crates/miden-testing/tests/scripts/rbac.rs | 7 +- 8 files changed, 543 insertions(+), 133 deletions(-) diff --git a/crates/miden-standards/src/account/access/authority.rs b/crates/miden-standards/src/account/access/authority.rs index 3bca21f2c6..7f11242672 100644 --- a/crates/miden-standards/src/account/access/authority.rs +++ b/crates/miden-standards/src/account/access/authority.rs @@ -48,13 +48,28 @@ const RBAC_CONTROLLED: u8 = 2; /// the MASM helper `authority::assert_authorized`. Installing the [`Authority`] component on an /// account thus selects the gating mode for *all* such procedures in one place. /// +/// # Safety invariant for [`Authority::AuthControlled`] +/// +/// Because `assert_authorized` is a no-op under `AuthControlled`, the account's auth component +/// is the **sole** gate for every authority-gated setter. The auth component MUST therefore +/// authenticate every such setter root, otherwise the setters become permissionless. Factories +/// that compose accounts under this variant enforce this invariant (see +/// [`create_fungible_faucet`][crate::account::faucets::create_fungible_faucet], which rejects +/// [`AuthMethod::NoAuth`][crate::AuthMethod::NoAuth] under +/// [`AccessControl::AuthControlled`][crate::account::access::AccessControl::AuthControlled] and +/// installs [`AuthSingleSigAcl`][crate::account::auth::AuthSingleSigAcl] with the complete +/// authority-gated trigger list for [`AuthMethod::SingleSig`][crate::AuthMethod::SingleSig]). +/// Custom account assemblies that install `Authority::AuthControlled` directly bear the same +/// responsibility. +/// /// Storage layout: `[authority, role_symbol_or_zero, 0, 0]` — single Word. #[repr(u8)] #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] pub enum Authority { /// Authority is the account's auth component; no extra check is performed by - /// `authority::assert_authorized`. + /// `authority::assert_authorized`. See the type-level docs for the safety invariant the + /// auth component must uphold. AuthControlled = AUTH_CONTROLLED, /// Authority is the [`Ownable2Step`][crate::account::access::Ownable2Step] owner; the call /// must be sent by the registered owner. diff --git a/crates/miden-standards/src/account/access/mod.rs b/crates/miden-standards/src/account/access/mod.rs index 085f18d847..37a76a17f2 100644 --- a/crates/miden-standards/src/account/access/mod.rs +++ b/crates/miden-standards/src/account/access/mod.rs @@ -2,51 +2,78 @@ use alloc::vec; use miden_protocol::account::{AccountComponent, AccountId, RoleSymbol}; +use crate::auth_method::AuthMethod; + pub mod authority; pub mod ownable2step; pub mod rbac; /// Access control configuration for account components. /// -/// Each variant expands into the set of [`AccountComponent`]s that implement that access -/// control choice **plus** the matching [`Authority`] component. The [`Authority`] is -/// auto-yielded so callers don't need to remember to install it separately and so that the -/// authority discriminator stays in sync with the chosen access mode. +/// Bundles two related concerns into a single declarative value: +/// +/// 1. **Account-level authentication** ([`AuthMethod`]) — selects the auth component that gates +/// every transaction executed against the account (signature, network-account allowlist, +/// nonce-only, ...). +/// 2. **Setter access gate** — selects the [`Authority`] policy consulted by `set_*` procedures +/// such as `set_max_supply`, `set_mint_policy`, and the token metadata setters. Each variant of +/// this enum picks a different gate; see the variants below. +/// +/// Each variant carries an `auth: AuthMethod` field for concern (1). The variant itself +/// (`AuthControlled` / `Ownable2Step` / `Rbac`) drives concern (2). In `AuthControlled` the two +/// concerns coincide: the auth component **is** the setter gate, so the auth component must +/// authenticate every authority-gated setter (factories enforce this; see +/// [`create_fungible_faucet`][crate::account::faucets::create_fungible_faucet]). /// -/// - [`AccessControl::AuthControlled`] yields just [`Authority::AuthControlled`]. -/// - [`AccessControl::Ownable2Step`] yields [`Ownable2Step`] + [`Authority::OwnerControlled`]. -/// - [`AccessControl::Rbac`] yields [`Ownable2Step`] + [`RoleBasedAccessControl`] + an -/// [`Authority`]. The `authority_role` field selects which authority kind is installed: +/// The variants expand into: +/// - [`AccessControl::AuthControlled`] → [`Authority::AuthControlled`] (only). The setter gate +/// delegates to the auth component. +/// - [`AccessControl::Ownable2Step`] → [`Ownable2Step`] + [`Authority::OwnerControlled`]. The +/// setter gate enforces `sender == owner`. +/// - [`AccessControl::Rbac`] → [`Ownable2Step`] + [`RoleBasedAccessControl`] + an [`Authority`]. +/// The `authority_role` field selects which authority kind is installed: /// - `None` → [`Authority::OwnerControlled`] (the top-level owner gates `set_*` operations). /// - `Some(role)` → [`Authority::RbacControlled { role }`] (any holder of `role` gates `set_*` /// operations). /// -/// Pass to -/// [`AccountBuilder::with_components`][miden_protocol::account::AccountBuilder::with_components] -/// to install the access control components on the account: +/// Note that the auth component is **not** yielded by [`IntoIterator`]; it is constructed +/// separately by factory functions +/// ([`create_fungible_faucet`][crate::account::faucets::create_fungible_faucet]) +/// from [`Self::auth_method`] and installed via +/// [`AccountBuilder::with_auth_component`][miden_protocol::account::AccountBuilder::with_auth_component]. +/// The iterator only yields the setter-gate components. /// /// ```no_run /// use miden_protocol::account::AccountBuilder; +/// use miden_standards::AuthMethod; /// use miden_standards::account::access::AccessControl; /// # let owner: miden_protocol::account::AccountId = unimplemented!(); /// # let init_seed = [0u8; 32]; -/// AccountBuilder::new(init_seed) -/// .with_components(AccessControl::Rbac { owner, authority_role: None }); +/// AccountBuilder::new(init_seed).with_components(AccessControl::Rbac { +/// owner, +/// authority_role: None, +/// auth: AuthMethod::NoAuth, +/// }); /// ``` -/// -/// For accounts that don't use the [`AccessControl`] convenience but want to install the -/// [`Authority`] component directly, the [`Authority`] enum can be passed via -/// [`AccountBuilder::with_component`][miden_protocol::account::AccountBuilder::with_component]. #[derive(Debug, Clone, PartialEq, Eq)] pub enum AccessControl { - /// No external access control component is installed; access decisions are gated solely - /// by the account's auth component. - AuthControlled, - /// Two-step ownership transfer with the provided initial owner. Authority for `set_*` - /// operations is fixed to the registered owner. - Ownable2Step { owner: AccountId }, + /// No external setter-gate component is installed; the account's auth component is the + /// sole gate for both transaction-level authentication and authority-gated setters. + /// + /// Because `Authority::AuthControlled` makes `assert_authorized` a no-op, the auth + /// component **must** authenticate every authority-gated setter root for setters to be + /// safe. Factories that build accounts under this variant reject auth methods that + /// cannot meet that invariant (for example, + /// [`create_fungible_faucet`][crate::account::faucets::create_fungible_faucet] rejects + /// [`AuthMethod::NoAuth`]). + AuthControlled { auth: AuthMethod }, + /// Two-step ownership transfer with the provided initial owner. The setter gate enforces + /// `sender == owner`; the auth component on `auth` only governs the faucet's own + /// transaction authentication. + Ownable2Step { owner: AccountId, auth: AuthMethod }, /// Role-based access control. Includes [`Ownable2Step`] internally; the provided `owner` - /// becomes the top-level RBAC authority (the account's owner). + /// becomes the top-level RBAC authority (the account's owner). `auth` governs the + /// account's own transaction authentication only. /// /// `authority_role` controls which authority is installed alongside RBAC: /// - `None` (default) → [`Authority::OwnerControlled`]: the top-level `owner` is the sole @@ -59,29 +86,51 @@ pub enum AccessControl { Rbac { owner: AccountId, authority_role: Option, + auth: AuthMethod, }, } +impl AccessControl { + /// Returns the [`AuthMethod`] selected for the account's auth component. Factories use + /// this to construct the auth component before installing the setter-gate components + /// yielded by [`IntoIterator`]. + pub fn auth_method(&self) -> &AuthMethod { + match self { + AccessControl::AuthControlled { auth } + | AccessControl::Ownable2Step { auth, .. } + | AccessControl::Rbac { auth, .. } => auth, + } + } +} + impl IntoIterator for AccessControl { type Item = AccountComponent; type IntoIter = alloc::vec::IntoIter; - /// Yields the [`AccountComponent`]s implementing this access control configuration, in the - /// order they must be installed on the account. The matching [`Authority`] component is - /// always included. + /// Yields the [`AccountComponent`]s implementing the **setter-gate** half of this access + /// control configuration. The auth component (concern (1) in the type-level docs) is + /// **not** yielded; callers obtain it from [`Self::auth_method`] and install it + /// separately via + /// [`AccountBuilder::with_auth_component`][miden_protocol::account::AccountBuilder::with_auth_component]. fn into_iter(self) -> Self::IntoIter { match self { - AccessControl::AuthControlled => vec![Authority::AuthControlled.into()].into_iter(), - AccessControl::Ownable2Step { owner } => { + AccessControl::AuthControlled { auth: _ } => { + vec![Authority::AuthControlled.into()].into_iter() + }, + AccessControl::Ownable2Step { owner, auth: _ } => { vec![Ownable2Step::new(owner).into(), Authority::OwnerControlled.into()].into_iter() }, - AccessControl::Rbac { owner, authority_role: None } => vec![ + AccessControl::Rbac { owner, authority_role: None, auth: _ } => vec![ Ownable2Step::new(owner).into(), RoleBasedAccessControl::empty().into(), Authority::OwnerControlled.into(), ] .into_iter(), - AccessControl::Rbac { owner, authority_role: Some(role) } => vec![ + AccessControl::Rbac { + owner, + authority_role: Some(role), + auth: _, + } => vec![ Ownable2Step::new(owner).into(), RoleBasedAccessControl::empty().into(), Authority::RbacControlled { role }.into(), diff --git a/crates/miden-standards/src/account/faucets/fungible/mod.rs b/crates/miden-standards/src/account/faucets/fungible/mod.rs index 65508d0bff..7eeadf0a0e 100644 --- a/crates/miden-standards/src/account/faucets/fungible/mod.rs +++ b/crates/miden-standards/src/account/faucets/fungible/mod.rs @@ -34,7 +34,13 @@ use super::{ }; use crate::account::access::AccessControl; use crate::account::account_component_code; -use crate::account::auth::{AuthNetworkAccount, AuthSingleSigAcl, AuthSingleSigAclConfig, NoAuth}; +use crate::account::auth::{ + AuthNetworkAccount, + AuthSingleSig, + AuthSingleSigAcl, + AuthSingleSigAclConfig, + NoAuth, +}; use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; use crate::account::policies::TokenPolicyManager; use crate::{AuthMethod, procedure_root}; @@ -77,6 +83,34 @@ procedure_root!( FungibleFaucet::code() ); +procedure_root!( + FUNGIBLE_FAUCET_SET_MAX_SUPPLY, + FungibleFaucet::NAME, + FungibleFaucet::SET_MAX_SUPPLY_PROC_NAME, + FungibleFaucet::code() +); + +procedure_root!( + FUNGIBLE_FAUCET_SET_DESCRIPTION, + FungibleFaucet::NAME, + FungibleFaucet::SET_DESCRIPTION_PROC_NAME, + FungibleFaucet::code() +); + +procedure_root!( + FUNGIBLE_FAUCET_SET_LOGO_URI, + FungibleFaucet::NAME, + FungibleFaucet::SET_LOGO_URI_PROC_NAME, + FungibleFaucet::code() +); + +procedure_root!( + FUNGIBLE_FAUCET_SET_EXTERNAL_LINK, + FungibleFaucet::NAME, + FungibleFaucet::SET_EXTERNAL_LINK_PROC_NAME, + FungibleFaucet::code() +); + /// An [`AccountComponent`] implementing a fungible faucet. /// /// This component bundles the asset minting/burning procedures and the token metadata @@ -192,6 +226,10 @@ impl FungibleFaucet { const MINT_PROC_NAME: &'static str = "mint_and_send"; const RECEIVE_AND_BURN_PROC_NAME: &'static str = "receive_and_burn"; + const SET_MAX_SUPPLY_PROC_NAME: &'static str = "set_max_supply"; + const SET_DESCRIPTION_PROC_NAME: &'static str = "set_description"; + const SET_LOGO_URI_PROC_NAME: &'static str = "set_logo_uri"; + const SET_EXTERNAL_LINK_PROC_NAME: &'static str = "set_external_link"; // CONSTRUCTORS // -------------------------------------------------------------------------------------------- @@ -248,6 +286,29 @@ impl FungibleFaucet { *FUNGIBLE_FAUCET_RECEIVE_AND_BURN } + /// Returns the procedure root of the `set_max_supply` account procedure. This is an + /// authority-gated setter; under + /// [`AccessControl::AuthControlled`][crate::account::access::AccessControl::AuthControlled] + /// it must appear in the auth component's trigger procedure list. + pub fn set_max_supply_root() -> AccountProcedureRoot { + *FUNGIBLE_FAUCET_SET_MAX_SUPPLY + } + + /// Returns the procedure root of the `set_description` account procedure. Authority-gated. + pub fn set_description_root() -> AccountProcedureRoot { + *FUNGIBLE_FAUCET_SET_DESCRIPTION + } + + /// Returns the procedure root of the `set_logo_uri` account procedure. Authority-gated. + pub fn set_logo_uri_root() -> AccountProcedureRoot { + *FUNGIBLE_FAUCET_SET_LOGO_URI + } + + /// Returns the procedure root of the `set_external_link` account procedure. Authority-gated. + pub fn set_external_link_root() -> AccountProcedureRoot { + *FUNGIBLE_FAUCET_SET_EXTERNAL_LINK + } + /// Returns the [`StorageSlotName`] holding the token config word /// `[token_supply, max_supply, decimals, token_symbol]`. pub fn token_config_slot() -> &'static StorageSlotName { @@ -497,61 +558,57 @@ impl TryFrom<&Account> for FungibleFaucet { // FACTORY // ================================================================================================ +/// Returns every authority-gated setter procedure root exported by a fungible faucet account. +/// +/// Under [`AccessControl::AuthControlled`] the auth component must authenticate calls to all +/// of these procedures, otherwise the setters become permissionless. This list is the single +/// source of truth used by [`create_fungible_faucet`] when configuring +/// [`AuthSingleSigAcl`]'s trigger procedure list. +/// +/// Includes `mint_and_send` so that minting always requires a signature regardless of access +/// control configuration. `receive_and_burn` is intentionally **excluded**: it is only +/// invoked from a note context (i.e., by an incoming burn note), and faucets accept those +/// without a signature. +fn all_authority_gated_setter_roots() -> Vec { + vec![ + FungibleFaucet::mint_and_send_root(), + FungibleFaucet::set_max_supply_root(), + FungibleFaucet::set_description_root(), + FungibleFaucet::set_logo_uri_root(), + FungibleFaucet::set_external_link_root(), + TokenPolicyManager::set_mint_policy_root(), + TokenPolicyManager::set_burn_policy_root(), + TokenPolicyManager::set_send_policy_root(), + TokenPolicyManager::set_receive_policy_root(), + ] +} + /// Creates a new fungible faucet account by composing the required components. /// -/// The behaviour of the resulting faucet (basic vs network-style) is determined entirely by the -/// combination of arguments passed in: -/// - `storage_mode`: typically [`AccountStorageMode::Public`] for basic or network faucets. -/// - `auth_method`: typically [`AuthMethod::SingleSig`] for basic faucets, or -/// [`AuthMethod::NetworkAccount`] for network-style faucets. [`AuthMethod::NoAuth`] is also -/// accepted for unauthenticated faucets. -/// - `access_control`: [`AccessControl::AuthControlled`] for auth-only faucets, or -/// [`AccessControl::Ownable2Step`] / [`AccessControl::Rbac`] for owner-controlled faucets. -/// - `token_policy_manager`: the unified [`TokenPolicyManager`] holding both mint and burn policy. +/// The behaviour of the resulting faucet is determined entirely by the +/// `access_control` argument, which carries both the setter-access policy and the +/// account-level [`AuthMethod`] used for transaction authentication: +/// - [`AccessControl::AuthControlled { auth }`] — auth-only faucets. +/// - With [`AuthMethod::SingleSig`], an [`AuthSingleSigAcl`] is installed whose trigger procedure +/// list contains **every** authority-gated setter (see [`all_authority_gated_setter_roots`]). +/// - With [`AuthMethod::NetworkAccount`], an [`AuthNetworkAccount`] is installed. The caller is +/// responsible for choosing `allowed_script_roots` that prevent unauthorized setter +/// invocations. +/// - [`AccessControl::Ownable2Step { owner, auth }`] / [`AccessControl::Rbac { .., auth }`] — +/// owner- or role-controlled faucets. The setter gate enforces `sender == owner` or RBAC role +/// membership in-procedure, so the auth component only governs the faucet's own transaction +/// authentication; any [`AuthMethod`] (including [`AuthMethod::NoAuth`]) is permitted. /// /// The faucet itself, including all token metadata, is provided in the `faucet` parameter (see /// [`FungibleFaucet::builder`]). pub fn create_fungible_faucet( init_seed: [u8; 32], faucet: FungibleFaucet, - storage_mode: AccountStorageMode, - auth_method: AuthMethod, access_control: AccessControl, token_policy_manager: TokenPolicyManager, + storage_mode: AccountStorageMode, ) -> Result { - let mint_proc_root = FungibleFaucet::mint_and_send_root(); - - let auth_component: AccountComponent = match auth_method { - AuthMethod::SingleSig { approver: (pub_key, auth_scheme) } => AuthSingleSigAcl::new( - pub_key, - auth_scheme, - AuthSingleSigAclConfig::new() - .with_auth_trigger_procedures(vec![mint_proc_root]) - .with_allow_unauthorized_input_notes(true), - ) - .map_err(FungibleFaucetError::AccountError)? - .into(), - AuthMethod::NoAuth => NoAuth::new().into(), - AuthMethod::NetworkAccount { allowed_script_roots } => { - AuthNetworkAccount::with_allowlist(allowed_script_roots) - .map_err(|err| { - FungibleFaucetError::UnsupportedAuthMethod(alloc::format!( - "invalid network account allowlist: {err}" - )) - })? - .into() - }, - AuthMethod::Unknown => { - return Err(FungibleFaucetError::UnsupportedAuthMethod( - "fungible faucets cannot be created with Unknown authentication method".into(), - )); - }, - AuthMethod::Multisig { .. } => { - return Err(FungibleFaucetError::UnsupportedAuthMethod( - "fungible faucets do not support Multisig authentication".into(), - )); - }, - }; + let auth_component = build_auth_component(&access_control)?; let account = AccountBuilder::new(init_seed) .account_type(AccountType::FungibleFaucet) @@ -565,3 +622,87 @@ pub fn create_fungible_faucet( Ok(account) } + +/// Builds the account-level auth component from the [`AuthMethod`] embedded in +/// `access_control`. The construction is variant-specific: +/// +/// - Under [`AccessControl::AuthControlled`], [`AuthSingleSig`] is wrapped in an +/// [`AuthSingleSigAcl`] whose trigger procedure list contains every authority-gated setter root, +/// ensuring the auth component authenticates every privileged state mutation. +/// - Under [`AccessControl::Ownable2Step`] / [`AccessControl::Rbac`], the setter gate is handled +/// in-procedure by `authority::assert_authorized`, so the auth component only needs to +/// authenticate the account's own transactions. A plain [`AuthSingleSig`] is installed for +/// [`AuthMethod::SingleSig`]. +/// +/// Rejects [`AuthMethod::Multisig`] / [`AuthMethod::Unknown`] for all variants (faucets do +/// not support Multisig today), and rejects [`AuthMethod::NoAuth`] specifically under +/// [`AccessControl::AuthControlled`] because it would leave authority-gated setters +/// permissionless. +fn build_auth_component( + access_control: &AccessControl, +) -> Result { + let auth = access_control.auth_method(); + + if let AccessControl::AuthControlled { .. } = access_control { + // AuthControlled: the auth component is the only setter gate. + return match auth { + AuthMethod::SingleSig { approver: (pub_key, auth_scheme) } => { + let component = AuthSingleSigAcl::new( + *pub_key, + *auth_scheme, + AuthSingleSigAclConfig::new() + .with_auth_trigger_procedures(all_authority_gated_setter_roots()) + .with_allow_unauthorized_input_notes(true), + ) + .map_err(FungibleFaucetError::AccountError)? + .into(); + Ok(component) + }, + AuthMethod::NetworkAccount { allowed_script_roots } => { + let component = AuthNetworkAccount::with_allowlist(allowed_script_roots.clone()) + .map_err(|err| { + FungibleFaucetError::UnsupportedAuthMethod(alloc::format!( + "invalid network account allowlist: {err}" + )) + })? + .into(); + Ok(component) + }, + AuthMethod::NoAuth => Err(FungibleFaucetError::IncompatibleAuthControlledAuth( + "NoAuth cannot authenticate authority-gated setters under AuthControlled; \ + use AccessControl::Ownable2Step or AccessControl::Rbac for owner-gated faucets, \ + or pair AuthControlled with SingleSig / NetworkAccount." + .into(), + )), + AuthMethod::Multisig { .. } => Err(FungibleFaucetError::UnsupportedAuthMethod( + "fungible faucets do not support Multisig authentication".into(), + )), + AuthMethod::Unknown => Err(FungibleFaucetError::UnsupportedAuthMethod( + "fungible faucets cannot be created with Unknown authentication method".into(), + )), + }; + } + + match auth { + AuthMethod::SingleSig { approver: (pub_key, auth_scheme) } => { + Ok(AuthSingleSig::new(*pub_key, *auth_scheme).into()) + }, + AuthMethod::NoAuth => Ok(NoAuth::new().into()), + AuthMethod::NetworkAccount { allowed_script_roots } => { + let component = AuthNetworkAccount::with_allowlist(allowed_script_roots.clone()) + .map_err(|err| { + FungibleFaucetError::UnsupportedAuthMethod(alloc::format!( + "invalid network account allowlist: {err}" + )) + })? + .into(); + Ok(component) + }, + AuthMethod::Multisig { .. } => Err(FungibleFaucetError::UnsupportedAuthMethod( + "fungible faucets do not support Multisig authentication".into(), + )), + AuthMethod::Unknown => Err(FungibleFaucetError::UnsupportedAuthMethod( + "fungible faucets cannot be created with Unknown authentication method".into(), + )), + } +} diff --git a/crates/miden-standards/src/account/faucets/fungible/tests.rs b/crates/miden-standards/src/account/faucets/fungible/tests.rs index a59e9153e6..6dd3f16266 100644 --- a/crates/miden-standards/src/account/faucets/fungible/tests.rs +++ b/crates/miden-standards/src/account/faucets/fungible/tests.rs @@ -1,3 +1,5 @@ +use alloc::collections::BTreeSet; + use assert_matches::assert_matches; use miden_protocol::account::auth::{AuthScheme, PublicKeyCommitment}; use miden_protocol::account::{AccountBuilder, AccountStorageMode, AccountType}; @@ -18,10 +20,53 @@ use crate::account::policies::{ }; use crate::account::wallets::BasicWallet; +/// Builds a minimal policy manager with AllowAll on every kind, used by the construction tests. +fn allow_all_policy_manager() -> TokenPolicyManager { + TokenPolicyManager::new() + .with_mint_policy(MintPolicyConfig::AllowAll, PolicyRegistration::Active) + .unwrap() + .with_burn_policy(BurnPolicyConfig::AllowAll, PolicyRegistration::Active) + .unwrap() + .with_send_policy(TransferPolicy::AllowAll, PolicyRegistration::Active) + .unwrap() + .with_receive_policy(TransferPolicy::AllowAll, PolicyRegistration::Active) + .unwrap() +} + +/// Builds a sample `FungibleFaucet` shared by construction tests. +fn sample_faucet() -> FungibleFaucet { + FungibleFaucet::builder() + .name(TokenName::new("polygon").unwrap()) + .symbol(TokenSymbol::try_from("POL").unwrap()) + .decimals(2) + .max_supply(AssetAmount::from(123u32)) + .description(Description::new("A polygon token").unwrap()) + .build() + .unwrap() +} + +/// Reads every trigger-procedure-root map entry from `0..num` and returns the set. +fn read_trigger_procedure_roots( + account: &miden_protocol::account::Account, + num: u32, +) -> BTreeSet { + (0..num) + .map(|i| { + account + .storage() + .get_map_item( + AuthSingleSigAcl::trigger_procedure_roots_slot(), + [Felt::from(i), Felt::ZERO, Felt::ZERO, Felt::ZERO].into(), + ) + .unwrap() + }) + .collect() +} + #[test] fn faucet_contract_creation() { let pub_key_word = Word::new([Felt::ONE; 4]); - let auth_method: AuthMethod = AuthMethod::SingleSig { + let auth = AuthMethod::SingleSig { approver: (pub_key_word.into(), AuthScheme::Falcon512Poseidon2), }; @@ -31,39 +76,18 @@ fn faucet_contract_creation() { 204, 149, 90, 166, 68, 100, 73, 106, 168, 125, 237, 138, 16, ]; - let max_supply = AssetAmount::from(123u32); let token_symbol_string = "POL"; let token_symbol = TokenSymbol::try_from(token_symbol_string).unwrap(); let token_name_string = "polygon"; let description_string = "A polygon token"; - let decimals = 2u8; - let storage_mode = AccountStorageMode::Private; - let token_name = TokenName::new(token_name_string).unwrap(); - let description = Description::new(description_string).unwrap(); - let faucet = FungibleFaucet::builder() - .name(token_name) - .symbol(token_symbol.clone()) - .decimals(decimals) - .max_supply(max_supply) - .description(description) - .build() - .unwrap(); + let faucet = sample_faucet(); let faucet_account = create_fungible_faucet( init_seed, faucet, - storage_mode, - auth_method, - AccessControl::AuthControlled, - TokenPolicyManager::new() - .with_mint_policy(MintPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap() - .with_burn_policy(BurnPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap() - .with_send_policy(TransferPolicy::AllowAll, PolicyRegistration::Active) - .unwrap() - .with_receive_policy(TransferPolicy::AllowAll, PolicyRegistration::Active) - .unwrap(), + AccessControl::AuthControlled { auth }, + allow_all_policy_manager(), + AccountStorageMode::Private, ) .unwrap(); @@ -76,25 +100,31 @@ fn faucet_contract_creation() { // The config slot of the auth component stores: // [num_trigger_procs, allow_unauthorized_output_notes, allow_unauthorized_input_notes, 0]. // - // With 1 trigger procedure (mint_and_send), allow_unauthorized_output_notes=false, and - // allow_unauthorized_input_notes=true, this should be [1, 0, 1, 0]. + // With 9 authority-gated trigger procedures (mint_and_send + 4 token metadata setters + + // 4 policy setters), allow_unauthorized_output_notes=false, and + // allow_unauthorized_input_notes=true, this should be [9, 0, 1, 0]. assert_eq!( faucet_account.storage().get_item(AuthSingleSigAcl::config_slot()).unwrap(), - [Felt::ONE, Felt::ZERO, Felt::ONE, Felt::ZERO].into() + [Felt::from(9_u32), Felt::ZERO, Felt::ONE, Felt::ZERO].into() ); - // The procedure root map should contain the mint_and_send procedure root. - let mint_root = FungibleFaucet::mint_and_send_root(); - assert_eq!( - faucet_account - .storage() - .get_map_item( - AuthSingleSigAcl::trigger_procedure_roots_slot(), - [Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ZERO].into() - ) - .unwrap(), - mint_root.as_word() - ); + // The trigger procedure root map should contain every authority-gated setter root. + let stored_roots = read_trigger_procedure_roots(&faucet_account, 9); + let expected_roots: BTreeSet = [ + FungibleFaucet::mint_and_send_root(), + FungibleFaucet::set_max_supply_root(), + FungibleFaucet::set_description_root(), + FungibleFaucet::set_logo_uri_root(), + FungibleFaucet::set_external_link_root(), + TokenPolicyManager::set_mint_policy_root(), + TokenPolicyManager::set_burn_policy_root(), + TokenPolicyManager::set_send_policy_root(), + TokenPolicyManager::set_receive_policy_root(), + ] + .into_iter() + .map(|root| root.as_word()) + .collect(); + assert_eq!(stored_roots, expected_roots); // Check that faucet metadata was initialized to the given values. // Storage layout: [token_supply, max_supply, decimals, symbol] @@ -122,6 +152,75 @@ fn faucet_contract_creation() { let _faucet_component = FungibleFaucet::try_from(faucet_account.clone()).unwrap(); } +/// `AccessControl::AuthControlled { auth: NoAuth }` must be rejected: under AuthControlled the +/// auth component is the sole gate for authority-gated setters, so a NoAuth pairing would +/// leave them permissionless. +#[test] +fn auth_controlled_rejects_no_auth() { + let err = create_fungible_faucet( + [7u8; 32], + sample_faucet(), + AccessControl::AuthControlled { auth: AuthMethod::NoAuth }, + allow_all_policy_manager(), + AccountStorageMode::Private, + ) + .expect_err("AuthControlled+NoAuth should be rejected"); + assert_matches!(err, FungibleFaucetError::IncompatibleAuthControlledAuth(_)); +} + +/// `AccessControl::AuthControlled { auth: Multisig | Unknown }` must be rejected — both are +/// already unsupported for fungible faucets, and the merge surfaces the rejection at the +/// type level. +#[test] +fn auth_controlled_rejects_multisig_and_unknown() { + let multisig_err = create_fungible_faucet( + [7u8; 32], + sample_faucet(), + AccessControl::AuthControlled { + auth: AuthMethod::Multisig { + threshold: 1, + approvers: alloc::vec::Vec::new(), + }, + }, + allow_all_policy_manager(), + AccountStorageMode::Private, + ) + .expect_err("AuthControlled+Multisig should be rejected"); + assert_matches!(multisig_err, FungibleFaucetError::UnsupportedAuthMethod(_)); + + let unknown_err = create_fungible_faucet( + [7u8; 32], + sample_faucet(), + AccessControl::AuthControlled { auth: AuthMethod::Unknown }, + allow_all_policy_manager(), + AccountStorageMode::Private, + ) + .expect_err("AuthControlled+Unknown should be rejected"); + assert_matches!(unknown_err, FungibleFaucetError::UnsupportedAuthMethod(_)); +} + +/// `Ownable2Step + NoAuth` is a valid configuration: the setter gate is enforced +/// in-procedure (`assert_sender_is_owner`), so the account-level auth can legitimately be +/// NoAuth (typical for network-style faucets driven by allowlisted note scripts). +#[test] +fn ownable2step_with_no_auth_is_accepted() { + use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; + + let owner = miden_protocol::account::AccountId::try_from( + ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, + ) + .unwrap(); + + let _account = create_fungible_faucet( + [7u8; 32], + sample_faucet(), + AccessControl::Ownable2Step { owner, auth: AuthMethod::NoAuth }, + allow_all_policy_manager(), + AccountStorageMode::Public, + ) + .expect("Ownable2Step+NoAuth should be accepted"); +} + #[test] fn faucet_create_from_account() { // prepare the test data @@ -168,4 +267,12 @@ fn faucet_create_from_account() { fn get_faucet_procedures() { let _mint_and_send_root = FungibleFaucet::mint_and_send_root(); let _receive_and_burn_root = FungibleFaucet::receive_and_burn_root(); + let _set_max_supply_root = FungibleFaucet::set_max_supply_root(); + let _set_description_root = FungibleFaucet::set_description_root(); + let _set_logo_uri_root = FungibleFaucet::set_logo_uri_root(); + let _set_external_link_root = FungibleFaucet::set_external_link_root(); + let _set_mint_policy_root = TokenPolicyManager::set_mint_policy_root(); + let _set_burn_policy_root = TokenPolicyManager::set_burn_policy_root(); + let _set_send_policy_root = TokenPolicyManager::set_send_policy_root(); + let _set_receive_policy_root = TokenPolicyManager::set_receive_policy_root(); } diff --git a/crates/miden-standards/src/account/faucets/mod.rs b/crates/miden-standards/src/account/faucets/mod.rs index 3622589fcc..ce30510e9a 100644 --- a/crates/miden-standards/src/account/faucets/mod.rs +++ b/crates/miden-standards/src/account/faucets/mod.rs @@ -59,6 +59,12 @@ pub enum FungibleFaucetError { MissingFungibleFaucetInterface, #[error("unsupported authentication method: {0}")] UnsupportedAuthMethod(String), + #[error( + "AccessControl::AuthControlled is incompatible with the chosen auth method: {0}. \ + Under AuthControlled the auth component is the sole gate for authority-protected \ + setters. It must authenticate every authority-gated setter root." + )] + IncompatibleAuthControlledAuth(String), #[error("account creation failed")] AccountError(#[source] AccountError), #[error("account is not a fungible faucet account")] diff --git a/crates/miden-standards/src/account/policies/manager.rs b/crates/miden-standards/src/account/policies/manager.rs index 17cd0df7c2..426c05a518 100644 --- a/crates/miden-standards/src/account/policies/manager.rs +++ b/crates/miden-standards/src/account/policies/manager.rs @@ -37,6 +37,7 @@ use super::burn::BurnPolicyConfig; use super::mint::MintPolicyConfig; use super::transfer::{TransferAllowAll, TransferPolicy}; use crate::account::account_component_code; +use crate::procedure_root; // ERRORS // ================================================================================================ @@ -52,6 +53,44 @@ pub enum TokenPolicyManagerError { account_component_code!(POLICY_MANAGER_CODE, "faucets/policies/policy_manager.masl"); +// PROCEDURE ROOTS +// ================================================================================================ + +/// MASL library namespace that backs the component code installed on faucet accounts. Used +/// for procedure-root lookups. This is `miden::standards::components::*` and is distinct from +/// [`TokenPolicyManager::NAME`], which is a human-readable identifier mirroring the +/// standards-side MASM module path used for documentation and storage slot prefixes. +const POLICY_MANAGER_LIBRARY_PATH: &str = + "miden::standards::components::faucets::policies::policy_manager"; + +procedure_root!( + POLICY_MANAGER_SET_MINT_POLICY, + POLICY_MANAGER_LIBRARY_PATH, + TokenPolicyManager::SET_MINT_POLICY_PROC_NAME, + TokenPolicyManager::code() +); + +procedure_root!( + POLICY_MANAGER_SET_BURN_POLICY, + POLICY_MANAGER_LIBRARY_PATH, + TokenPolicyManager::SET_BURN_POLICY_PROC_NAME, + TokenPolicyManager::code() +); + +procedure_root!( + POLICY_MANAGER_SET_SEND_POLICY, + POLICY_MANAGER_LIBRARY_PATH, + TokenPolicyManager::SET_SEND_POLICY_PROC_NAME, + TokenPolicyManager::code() +); + +procedure_root!( + POLICY_MANAGER_SET_RECEIVE_POLICY, + POLICY_MANAGER_LIBRARY_PATH, + TokenPolicyManager::SET_RECEIVE_POLICY_PROC_NAME, + TokenPolicyManager::code() +); + // STORAGE SLOT NAMES // ================================================================================================ @@ -191,6 +230,11 @@ impl TokenPolicyManager { /// Component description used in [`AccountComponentMetadata`]. pub const DESCRIPTION: &'static str = "Token policy manager for fungible faucets"; + const SET_MINT_POLICY_PROC_NAME: &'static str = "set_mint_policy"; + const SET_BURN_POLICY_PROC_NAME: &'static str = "set_burn_policy"; + const SET_SEND_POLICY_PROC_NAME: &'static str = "set_send_policy"; + const SET_RECEIVE_POLICY_PROC_NAME: &'static str = "set_receive_policy"; + // CONSTRUCTORS // -------------------------------------------------------------------------------------------- @@ -374,6 +418,29 @@ impl TokenPolicyManager { .collect() } + /// Returns the procedure root of the `set_mint_policy` account procedure. This is an + /// authority-gated setter; under + /// [`AccessControl::AuthControlled`][crate::account::access::AccessControl::AuthControlled] + /// it must appear in the auth component's trigger procedure list. + pub fn set_mint_policy_root() -> AccountProcedureRoot { + *POLICY_MANAGER_SET_MINT_POLICY + } + + /// Returns the procedure root of the `set_burn_policy` account procedure. Authority-gated. + pub fn set_burn_policy_root() -> AccountProcedureRoot { + *POLICY_MANAGER_SET_BURN_POLICY + } + + /// Returns the procedure root of the `set_send_policy` account procedure. Authority-gated. + pub fn set_send_policy_root() -> AccountProcedureRoot { + *POLICY_MANAGER_SET_SEND_POLICY + } + + /// Returns the procedure root of the `set_receive_policy` account procedure. Authority-gated. + pub fn set_receive_policy_root() -> AccountProcedureRoot { + *POLICY_MANAGER_SET_RECEIVE_POLICY + } + /// Returns the [`StorageSlotName`] where the active mint policy procedure root is stored. pub fn active_mint_policy_slot() -> &'static StorageSlotName { &ACTIVE_MINT_POLICY_PROC_ROOT_SLOT_NAME diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 9c9e93c649..3f156f0588 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -47,6 +47,7 @@ use miden_protocol::testing::account_id::ACCOUNT_ID_FEE_FAUCET; use miden_protocol::testing::random_secret_key::random_secret_key; use miden_protocol::transaction::{OrderedTransactionHeaders, RawOutputNote, TransactionKernel}; use miden_protocol::{MAX_OUTPUT_NOTES_PER_BATCH, Word}; +use miden_standards::AuthMethod; use miden_standards::account::access::AccessControl; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; use miden_standards::account::policies::{ @@ -407,11 +408,17 @@ impl MockChainBuilder { .with_send_policy(TransferPolicy::AllowAll, PolicyRegistration::Active)? .with_receive_policy(TransferPolicy::AllowAll, PolicyRegistration::Active)?; + // The `auth` field on AccessControl is a placeholder in chain_builder: the real auth + // component is built from `auth_method: Auth` by `add_account_from_builder` and + // installed via `with_auth_component`. `AccessControl::IntoIterator` only yields the + // setter-gate components, so the placeholder is not used to construct components — it + // only satisfies the type. Tests that exercise factory-level validation should call + // `create_fungible_faucet` directly instead. self.add_existing_fungible_faucet( auth_method, faucet, AccountStorageMode::Public, - AccessControl::AuthControlled, + AccessControl::AuthControlled { auth: AuthMethod::NoAuth }, token_policy_manager, ) } @@ -456,16 +463,22 @@ impl MockChainBuilder { .with_send_policy(TransferPolicy::AllowAll, PolicyRegistration::Active)? .with_receive_policy(TransferPolicy::AllowAll, PolicyRegistration::Active)?; - let allowed_script_roots = allowed_script_roots - .into_iter() - .chain([MintNote::script_root(), BurnNote::script_root()]) - .collect(); + let allowed_script_roots: alloc::collections::BTreeSet = + allowed_script_roots + .into_iter() + .chain([MintNote::script_root(), BurnNote::script_root()]) + .collect(); self.add_existing_fungible_faucet( - Auth::NetworkAccount { allowed_script_roots }, + Auth::NetworkAccount { + allowed_script_roots: allowed_script_roots.clone(), + }, faucet, AccountStorageMode::Public, - AccessControl::Ownable2Step { owner: owner_account_id }, + AccessControl::Ownable2Step { + owner: owner_account_id, + auth: AuthMethod::NetworkAccount { allowed_script_roots }, + }, token_policy_manager, ) } @@ -488,16 +501,22 @@ impl MockChainBuilder { .with_send_policy(TransferPolicy::AllowAll, PolicyRegistration::Active)? .with_receive_policy(TransferPolicy::AllowAll, PolicyRegistration::Active)?; - let allowed_script_roots = allowed_script_roots - .into_iter() - .chain([MintNote::script_root(), BurnNote::script_root()]) - .collect(); + let allowed_script_roots: alloc::collections::BTreeSet = + allowed_script_roots + .into_iter() + .chain([MintNote::script_root(), BurnNote::script_root()]) + .collect(); self.add_existing_fungible_faucet( - Auth::NetworkAccount { allowed_script_roots }, + Auth::NetworkAccount { + allowed_script_roots: allowed_script_roots.clone(), + }, faucet, AccountStorageMode::Public, - AccessControl::Ownable2Step { owner: owner_account_id }, + AccessControl::Ownable2Step { + owner: owner_account_id, + auth: AuthMethod::NetworkAccount { allowed_script_roots }, + }, token_policy_manager, ) } @@ -528,11 +547,12 @@ impl MockChainBuilder { .with_send_policy(TransferPolicy::AllowAll, PolicyRegistration::Active)? .with_receive_policy(TransferPolicy::AllowAll, PolicyRegistration::Active)?; + // See note on `add_existing_basic_faucet` re: the placeholder auth field. self.create_new_fungible_faucet( auth_method, faucet, AccountStorageMode::Public, - AccessControl::AuthControlled, + AccessControl::AuthControlled { auth: AuthMethod::NoAuth }, token_policy_manager, ) } diff --git a/crates/miden-testing/tests/scripts/rbac.rs b/crates/miden-testing/tests/scripts/rbac.rs index 0f952c567c..c4f5bf213d 100644 --- a/crates/miden-testing/tests/scripts/rbac.rs +++ b/crates/miden-testing/tests/scripts/rbac.rs @@ -16,6 +16,7 @@ use miden_protocol::account::{ use miden_protocol::errors::AccountIdError; use miden_protocol::note::{Note, NoteType}; use miden_protocol::{Felt, Word}; +use miden_standards::AuthMethod; use miden_standards::account::access::{AccessControl, Ownable2Step, RoleBasedAccessControl}; use miden_standards::errors::standards::{ ERR_ACCOUNT_NOT_IN_ROLE, @@ -33,7 +34,11 @@ fn create_rbac_account_with_owner(owner: AccountId) -> anyhow::Result { let account = AccountBuilder::new([9; 32]) .storage_mode(AccountStorageMode::Public) .with_auth_component(Auth::IncrNonce) - .with_components(AccessControl::Rbac { owner, authority_role: None }) + .with_components(AccessControl::Rbac { + owner, + authority_role: None, + auth: AuthMethod::NoAuth, + }) .build_existing()?; Ok(account) From 8b2527b04895cf133189ace3a3d106ac07c7cdae Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 18 May 2026 16:52:44 +0200 Subject: [PATCH 02/12] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f62d53166..4f2090f221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,10 +59,12 @@ - [BREAKING] `FungibleAsset::amount()` and `AssetVault::get_balance()` now return `AssetAmount` ([#2928](https://github.com/0xMiden/protocol/pull/2928)). - [BREAKING] Upgraded `miden-vm` to v0.23 and `miden-crypto` to v0.25. Notable downstream changes: dropped the immediate form of `adv_push` in kernel and standards MASM, marked cross-module-referenced MASM constants and procedures `pub`, migrated to the split `Host`/`BaseHost` trait surface, renamed `Felt::new` call sites to the preserved-behavior `Felt::new_unchecked`, switched `ecdsa_k256_keccak`/`eddsa_25519_sha512` `SecretKey` references to the new `SigningKey`/`KeyExchangeKey` types, and recomputed the kernel's `EMPTY_SMT_ROOT` constant for the Plonky3-aligned Poseidon2 and domain-separated `SmtLeaf::hash` ([#2931](https://github.com/0xMiden/protocol/pull/2931)). - Derive `Hash` implementation for `StorageMapKey` and `StorageMapKeyHash` to allow using those values as keys in containers ([#2843](https://github.com/0xMiden/protocol/issues/2843)). +- [BREAKING] Merged `AuthMethod` into `AccessControl` so every variant (`AuthControlled`, `Ownable2Step`, `Rbac`) now includes an `auth: AuthMethod` field. ([#2944](https://github.com/0xMiden/protocol/pull/2944)). ### Fixes - Fixed `LocalTransactionProver` accumulating `MastForest` entries across `prove()` calls, causing `capacity_overflow` panics in WASM environments where linear memory fragmentation prevents subsequent allocations ([#2918](https://github.com/0xMiden/protocol/pull/2918)). +- Fixed `create_fungible_faucet` leaving authority-gated setters unauthenticated under `AccessControl::AuthControlled` ([#2943](https://github.com/0xMiden/protocol/issues/2943), [#2944](https://github.com/0xMiden/protocol/pull/2944)). - Made deserialization of `AccountCode` more robust ([#2788](https://github.com/0xMiden/protocol/pull/2788)). - Validated `PartialBlockchain` invariants on deserialization ([#2888](https://github.com/0xMiden/protocol/pull/2888)). - Fixed `output_note::add_asset` and `output_note::set_attachment` to no longer accept invalid note indices ([#2824](https://github.com/0xMiden/protocol/pull/2824)). From 039ced3ebbf3888424a7d12c5b604da391fa8087 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 18 May 2026 17:02:41 +0200 Subject: [PATCH 03/12] fix documentation --- crates/miden-standards/src/account/faucets/fungible/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/miden-standards/src/account/faucets/fungible/mod.rs b/crates/miden-standards/src/account/faucets/fungible/mod.rs index 7eeadf0a0e..ef97d1c159 100644 --- a/crates/miden-standards/src/account/faucets/fungible/mod.rs +++ b/crates/miden-standards/src/account/faucets/fungible/mod.rs @@ -590,7 +590,10 @@ fn all_authority_gated_setter_roots() -> Vec { /// account-level [`AuthMethod`] used for transaction authentication: /// - [`AccessControl::AuthControlled { auth }`] — auth-only faucets. /// - With [`AuthMethod::SingleSig`], an [`AuthSingleSigAcl`] is installed whose trigger procedure -/// list contains **every** authority-gated setter (see [`all_authority_gated_setter_roots`]). +/// list contains **every** authority-gated setter exported by the faucet and policy-manager +/// components (`mint_and_send`, `set_max_supply`, `set_description`, `set_logo_uri`, +/// `set_external_link`, `set_mint_policy`, `set_burn_policy`, `set_send_policy`, +/// `set_receive_policy`). /// - With [`AuthMethod::NetworkAccount`], an [`AuthNetworkAccount`] is installed. The caller is /// responsible for choosing `allowed_script_roots` that prevent unauthorized setter /// invocations. From c55cebd55dcad04f78b812f2b8090742196f67cf Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 1 Jun 2026 16:10:35 +0200 Subject: [PATCH 04/12] remove unnecessary comments --- .../src/account/auth/account_auth_component.rs | 10 +--------- .../src/account/faucets/fungible/mod.rs | 7 ++----- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/crates/miden-standards/src/account/auth/account_auth_component.rs b/crates/miden-standards/src/account/auth/account_auth_component.rs index a51b04ef2d..5bb710a8db 100644 --- a/crates/miden-standards/src/account/auth/account_auth_component.rs +++ b/crates/miden-standards/src/account/auth/account_auth_component.rs @@ -38,21 +38,13 @@ pub enum AccountAuthScheme { } /// A typed wrapper around an authentication [`AccountComponent`]. -/// -/// Replaces the previous `AuthMethod` enum: instead of describing what an auth component -/// _could_ be and translating to a concrete component inside each factory, callers construct -/// the auth component themselves with one of the convenience constructors below (or wrap a -/// custom [`AccountComponent`]) and pass the resulting [`AccountAuthComponent`] to a -/// factory. Factories use [`Self::scheme`] to validate the combination. pub struct AccountAuthComponent { inner: AccountComponent, scheme: AccountAuthScheme, } impl AccountAuthComponent { - /// Wraps an arbitrary [`AccountComponent`] as an auth component. The component is treated - /// as opaque ([`AccountAuthScheme::Custom`]) and is accepted by factories that allow - /// custom auth components. + /// Wraps an arbitrary [`AccountComponent`] as an auth component. pub fn custom(component: AccountComponent) -> Self { Self { inner: component, diff --git a/crates/miden-standards/src/account/faucets/fungible/mod.rs b/crates/miden-standards/src/account/faucets/fungible/mod.rs index 9d886a3a44..9809805211 100644 --- a/crates/miden-standards/src/account/faucets/fungible/mod.rs +++ b/crates/miden-standards/src/account/faucets/fungible/mod.rs @@ -607,9 +607,8 @@ fn all_authority_gated_setter_roots() -> Vec { /// - [`AccountAuthScheme::NetworkAccount`]: rejected — network-style faucets must use /// [`create_network_fungible_faucet`] with [`AccessControl::Ownable2Step`] or /// [`AccessControl::Rbac`]. -/// - [`AccountAuthScheme::Custom`]: accepted; the caller is responsible for ensuring the custom +/// - [`AccountAuthScheme::Custom`]: accepted. The caller is responsible for ensuring the custom /// auth component authenticates every authority-gated setter root. -/// - Multisig variants: rejected via [`FungibleFaucetError::UnsupportedAuthMethod`]. pub fn create_user_fungible_faucet( init_seed: [u8; 32], faucet: FungibleFaucet, @@ -642,9 +641,7 @@ pub fn create_user_fungible_faucet( /// [`AccountAuthScheme::Custom`]: accepted. /// - [`AccountAuthScheme::SingleSig`], [`AccountAuthScheme::SingleSigAcl`]: rejected via /// [`FungibleFaucetError::UnsupportedAccessControlAuthCombination`] — SingleSig is for -/// user-account faucets (see [`create_user_fungible_faucet`]); pairing it here duplicates the -/// setter check with a per-tx signature that doesn't add security. -/// - Multisig variants: rejected via [`FungibleFaucetError::UnsupportedAuthMethod`]. +/// user-account faucets (see [`create_user_fungible_faucet`]). pub fn create_network_fungible_faucet( init_seed: [u8; 32], faucet: FungibleFaucet, From a8fc71e695d67ba6d349ed5baa237bf040d26302 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 1 Jun 2026 16:14:54 +0200 Subject: [PATCH 05/12] lint --- crates/miden-testing/tests/scripts/pausable.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/miden-testing/tests/scripts/pausable.rs b/crates/miden-testing/tests/scripts/pausable.rs index e12339036f..eb85517b50 100644 --- a/crates/miden-testing/tests/scripts/pausable.rs +++ b/crates/miden-testing/tests/scripts/pausable.rs @@ -16,8 +16,8 @@ use miden_protocol::errors::MasmError; use miden_protocol::note::{Note, NoteTag, NoteType}; use miden_protocol::transaction::RawOutputNote; use miden_protocol::utils::sync::LazyLock; -use miden_standards::account::access::AccessControl; use miden_standards::account::access::pausable::{PausableManager, PausableStorage}; +use miden_standards::account::access::{AccessControl, Authority}; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; use miden_standards::account::policies::{ BurnPolicyConfig, @@ -579,7 +579,7 @@ async fn pausable_set_max_supply_fails_when_paused() -> anyhow::Result<()> { // TESTS — PAUSABLE MANAGER WITH AUTH-CONTROLLED ACCESS CONTROL // ================================================================================================ -/// Same as `add_faucet_with_pause` but uses `AccessControl::AuthControlled`. +/// Same as `add_faucet_with_pause` but uses `Authority::AuthControlled` directly. fn add_faucet_with_pause_auth_controlled( builder: &mut MockChainBuilder, ) -> anyhow::Result { @@ -593,7 +593,7 @@ fn add_faucet_with_pause_auth_controlled( let account_builder = AccountBuilder::new([46u8; 32]) .account_type(AccountType::Public) .with_component(faucet) - .with_components(AccessControl::AuthControlled) + .with_component(Authority::AuthControlled) .with_component(PausableManager); builder.add_account_from_builder(Auth::IncrNonce, account_builder, AccountState::Exists) From 6c7a40532dad51964a22c9f4bef5e0ce36f518d8 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 1 Jun 2026 16:23:36 +0200 Subject: [PATCH 06/12] fix lint --- CHANGELOG.md | 1 - .../asm/account_components/access/pausable/manager.masm | 4 ++-- .../src/account/access/pausable/manager.rs | 8 ++++---- .../miden-standards/src/account/faucets/fungible/mod.rs | 5 +++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ce1023833..41ac24f3ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -114,7 +114,6 @@ - Bound MINT notes to their faucet ([#2911](https://github.com/0xMiden/protocol/pull/2911)). - Fixed `LocalTransactionProver` accumulating `MastForest` entries across `prove()` calls, causing `capacity_overflow` panics in WASM environments where linear memory fragmentation prevents subsequent allocations ([#2918](https://github.com/0xMiden/protocol/pull/2918)). - Fixed `TokenPolicyManager::manager_storage_slots` to register the protocol-reserved asset-callback storage slots whenever any transfer policy is configured (including `TransferAllowAll`), so every minted asset carries `AssetCallbackFlag::Enabled` and future `set_send_policy` / `set_receive_policy` switches apply uniformly to the entire circulating supply ([#2946](https://github.com/0xMiden/protocol/pull/2946)). -- Fixed `create_fungible_faucet` leaving authority-gated setters unauthenticated under `AccessControl::AuthControlled`: the `AuthSingleSigAcl` trigger list now contains every authority-gated setter root (`set_max_supply`, `set_description`, `set_logo_uri`, `set_external_link`, `set_mint_policy`, `set_burn_policy`, `set_send_policy`, `set_receive_policy`) in addition to `mint_and_send`. ([#2958](https://github.com/0xMiden/protocol/pull/2958)). - [BREAKING] Added missing transaction `ref_block_commitment` validation in `ProposedBatch::new` ([#2971](https://github.com/0xMiden/protocol/pull/2971)). ## 0.14.6 (2026-05-09) diff --git a/crates/miden-standards/asm/account_components/access/pausable/manager.masm b/crates/miden-standards/asm/account_components/access/pausable/manager.masm index b7e74886f0..ed3d462b92 100644 --- a/crates/miden-standards/asm/account_components/access/pausable/manager.masm +++ b/crates/miden-standards/asm/account_components/access/pausable/manager.masm @@ -4,8 +4,8 @@ # `Authority` component via `exec.authority::assert_authorized`. # # Companion components required: -# - `Authority` (installed via `AccessControl::Ownable2Step` / `AccessControl::Rbac` / -# `AccessControl::AuthControlled`). +# - `Authority` (installed via `AccessControl::Ownable2Step` / `AccessControl::Rbac`, or +# `Authority::AuthControlled` directly by `create_user_fungible_faucet`). # - `Pausable` — provides the `is_paused` storage slot. pub use ::miden::standards::access::pausable::manager::pause diff --git a/crates/miden-standards/src/account/access/pausable/manager.rs b/crates/miden-standards/src/account/access/pausable/manager.rs index ee3ee06c2c..b01c7e2bba 100644 --- a/crates/miden-standards/src/account/access/pausable/manager.rs +++ b/crates/miden-standards/src/account/access/pausable/manager.rs @@ -27,9 +27,9 @@ procedure_root!( /// [`crate::account::access::Authority`] component via `exec.authority::assert_authorized`. /// /// `PausableManager` works uniformly with every standard access scheme: -/// - [`crate::account::access::AccessControl::AuthControlled`] → -/// [`crate::account::access::Authority::AuthControlled`] gates pause / unpause via the account's -/// own auth component. +/// - [`crate::account::access::Authority::AuthControlled`] — installed directly by +/// [`crate::account::faucets::create_user_fungible_faucet`]; gates pause / unpause via the +/// account's own auth component. /// - [`crate::account::access::AccessControl::Ownable2Step`] → /// [`crate::account::access::Authority::OwnerControlled`] requires the Ownable2Step owner. /// - [`crate::account::access::AccessControl::Rbac`] → @@ -39,7 +39,7 @@ procedure_root!( /// /// Companion components required: /// - [`crate::account::access::Authority`] — installed automatically by the -/// [`crate::account::access::AccessControl`] enum. +/// [`crate::account::access::AccessControl`] enum (or directly by user-faucet factories). /// - [`super::Pausable`] — provides the `is_paused` storage slot that pause / unpause write to. #[derive(Debug, Clone, Copy, Default)] pub struct PausableManager; diff --git a/crates/miden-standards/src/account/faucets/fungible/mod.rs b/crates/miden-standards/src/account/faucets/fungible/mod.rs index 9809805211..ef7f016fce 100644 --- a/crates/miden-standards/src/account/faucets/fungible/mod.rs +++ b/crates/miden-standards/src/account/faucets/fungible/mod.rs @@ -392,7 +392,8 @@ impl FungibleFaucet { /// `execute_burn_policy`, `check_policy` (allow_all / blocklist / allowlist) and the metadata /// setters can read it without panicking. Pause / unpause administration is exposed by the /// [`crate::account::access::pausable::PausableManager`] component, which is bundled by - /// [`create_fungible_faucet`] alongside this faucet so the slot is always actionable. + /// [`create_user_fungible_faucet`] / [`create_network_fungible_faucet`] alongside this faucet + /// so the slot is always actionable. pub fn into_storage_slots(self) -> Vec { let mut slots: Vec = Vec::new(); slots.push(self.token_config_slot_value()); @@ -714,7 +715,7 @@ fn validate_network_faucet_auth( AccountAuthScheme::SingleSig | AccountAuthScheme::SingleSigAcl => { Err(FungibleFaucetError::UnsupportedAccessControlAuthCombination( "SingleSig is only supported with create_user_fungible_faucet \ - (AccessControl::AuthControlled)" + (Authority::AuthControlled)" .into(), )) }, From ab964d5ce6ee5bace1abb48bf887538d045746c9 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Wed, 3 Jun 2026 16:38:01 +0200 Subject: [PATCH 07/12] remove AccountAuthScheme and AccountAuthComponent --- CHANGELOG.md | 6 +- .../account/auth/account_auth_component.rs | 214 ------------------ .../miden-standards/src/account/auth/mod.rs | 3 - .../src/account/faucets/fungible/mod.rs | 128 ++--------- .../src/account/faucets/fungible/tests.rs | 94 +------- .../src/account/faucets/mod.rs | 8 - .../src/account/interface/component.rs | 20 -- .../src/account/interface/extension.rs | 16 +- .../src/account/interface/mod.rs | 25 +- .../src/account/interface/test.rs | 142 ++---------- .../src/account/wallets/mod.rs | 50 +--- .../src/testing/account_interface.rs | 17 +- .../src/kernel_tests/tx/test_tx.rs | 17 +- crates/miden-testing/tests/wallet/mod.rs | 5 +- 14 files changed, 93 insertions(+), 652 deletions(-) delete mode 100644 crates/miden-standards/src/account/auth/account_auth_component.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 41ac24f3ef..90521bfbd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,8 +88,10 @@ - [BREAKING] `FungibleAsset::amount()` and `AssetVault::get_balance()` now return `AssetAmount` ([#2928](https://github.com/0xMiden/protocol/pull/2928)). - [BREAKING] Upgraded `miden-vm` to v0.23 and `miden-crypto` to v0.25. Notable downstream changes: dropped the immediate form of `adv_push` in kernel and standards MASM, marked cross-module-referenced MASM constants and procedures `pub`, migrated to the split `Host`/`BaseHost` trait surface, renamed `Felt::new` call sites to the preserved-behavior `Felt::new_unchecked`, switched `ecdsa_k256_keccak`/`eddsa_25519_sha512` `SecretKey` references to the new `SigningKey`/`KeyExchangeKey` types, and recomputed the kernel's `EMPTY_SMT_ROOT` constant for the Plonky3-aligned Poseidon2 and domain-separated `SmtLeaf::hash` ([#2931](https://github.com/0xMiden/protocol/pull/2931)). - Derive `Hash` implementation for `StorageMapKey` and `StorageMapKeyHash` to allow using those values as keys in containers ([#2843](https://github.com/0xMiden/protocol/issues/2843)). -- [BREAKING] Replaced `AuthMethod` enum with `AccountAuthComponent`, a thin wrapper around `AccountComponent` carrying an `AccountAuthScheme` tag. Removed `AccessControl::AuthControlled` variant entirely: user-facing faucets always install `Authority::AuthControlled` directly. ([#2944](https://github.com/0xMiden/protocol/pull/2944)). -- [BREAKING] Split `create_fungible_faucet` into `create_user_fungible_faucet` (AuthControlled-style) and `create_network_fungible_faucet` (Ownable2Step / Rbac). The user-facing factory validates that the `AccountAuthComponent` is `AuthSingleSigAcl`, rejecting plain `SingleSig`, `NoAuth`, and `NetworkAccount`. A new `user_faucet_single_sig_acl` convenience helper builds an `AuthSingleSigAcl` with the complete authority-gated setter trigger list. ([#2944](https://github.com/0xMiden/protocol/pull/2944)). +- [BREAKING] Removed `AuthMethod` enum and `AccessControl::AuthControlled` variant. Faucet factories now take concrete auth-component types so invalid configurations are rejected at compile time. ([#2944](https://github.com/0xMiden/protocol/pull/2944)). +- [BREAKING] Split `create_fungible_faucet` into `create_user_fungible_faucet` (takes `AuthSingleSigAcl`, installs `Authority::AuthControlled` directly) and `create_network_fungible_faucet` (takes `AuthNetworkAccount` and `AccessControl::Ownable2Step | Rbac`). For any other auth scheme, use `AccountBuilder` directly. A new `user_faucet_single_sig_acl` helper builds an `AuthSingleSigAcl` with the complete authority-gated setter trigger list. ([#2944](https://github.com/0xMiden/protocol/pull/2944)). +- [BREAKING] `create_basic_wallet` now takes `AuthSingleSig` directly instead of a wrapped auth type. For multisig wallets, use `AccountBuilder` directly. ([#2944](https://github.com/0xMiden/protocol/pull/2944)). +- [BREAKING] Removed `AccountInterface::auth()` method (returning `Vec`). Auth components are now discovered via `AccountInterface::auth_components()`, which iterates `AccountComponentInterface` variants flagged by `is_auth_component()`. ([#2944](https://github.com/0xMiden/protocol/pull/2944)). - Optimized `B2AGG` processing with selective load/save of Local Exit Tree frontier entries, halving frontier storage map syscalls ([#2752](https://github.com/0xMiden/protocol/pull/2752)). - [BREAKING] Removed `AccountType` ([#2939](https://github.com/0xMiden/protocol/pull/2939)). - [BREAKING] Removed `AccountType` and renamed `AccountStorageMode` to `AccountType` ([#2939](https://github.com/0xMiden/protocol/pull/2939), [#2942](https://github.com/0xMiden/protocol/pull/2942)). diff --git a/crates/miden-standards/src/account/auth/account_auth_component.rs b/crates/miden-standards/src/account/auth/account_auth_component.rs deleted file mode 100644 index 5bb710a8db..0000000000 --- a/crates/miden-standards/src/account/auth/account_auth_component.rs +++ /dev/null @@ -1,214 +0,0 @@ -use alloc::collections::BTreeSet; -use alloc::format; - -use miden_protocol::account::AccountComponent; -use miden_protocol::account::auth::{AuthScheme, PublicKeyCommitment}; -use miden_protocol::errors::AccountError; -use miden_protocol::note::NoteScriptRoot; - -use super::{ - AuthGuardedMultisig, - AuthGuardedMultisigConfig, - AuthMultisig, - AuthMultisigConfig, - AuthMultisigSmart, - AuthMultisigSmartConfig, - AuthNetworkAccount, - AuthSingleSig, - AuthSingleSigAcl, - AuthSingleSigAclConfig, - NoAuth, -}; - -/// Categorical tag describing which standard auth scheme an [`AccountAuthComponent`] wraps. -/// -/// Factory functions use this to validate `(access control, auth scheme)` combinations -/// without exposing the concrete component type. `Custom` covers any wrapper not produced -/// by the convenience constructors on [`AccountAuthComponent`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum AccountAuthScheme { - NoAuth, - SingleSig, - SingleSigAcl, - NetworkAccount, - Multisig, - GuardedMultisig, - MultisigSmart, - Custom, -} - -/// A typed wrapper around an authentication [`AccountComponent`]. -pub struct AccountAuthComponent { - inner: AccountComponent, - scheme: AccountAuthScheme, -} - -impl AccountAuthComponent { - /// Wraps an arbitrary [`AccountComponent`] as an auth component. - pub fn custom(component: AccountComponent) -> Self { - Self { - inner: component, - scheme: AccountAuthScheme::Custom, - } - } - - /// Builds a [`NoAuth`] component: the account performs only a nonce increment, no - /// signature verification. - pub fn no_auth() -> Self { - Self { - inner: NoAuth::new().into(), - scheme: AccountAuthScheme::NoAuth, - } - } - - /// Builds an [`AuthSingleSig`] component (per-tx signature with no procedure-level ACL). - pub fn single_sig(pub_key: PublicKeyCommitment, scheme: AuthScheme) -> Self { - Self { - inner: AuthSingleSig::new(pub_key, scheme).into(), - scheme: AccountAuthScheme::SingleSig, - } - } - - /// Builds an [`AuthSingleSigAcl`] component with the given configuration. Procedure roots - /// in the ACL's trigger list force signature verification when called. - pub fn single_sig_acl( - pub_key: PublicKeyCommitment, - scheme: AuthScheme, - config: AuthSingleSigAclConfig, - ) -> Result { - let component = AuthSingleSigAcl::new(pub_key, scheme, config)?.into(); - Ok(Self { - inner: component, - scheme: AccountAuthScheme::SingleSigAcl, - }) - } - - /// Builds an [`AuthNetworkAccount`] component restricted to the given note script roots. - /// The allowlist must be non-empty. - pub fn network_account( - allowed_script_roots: BTreeSet, - ) -> Result { - let component = AuthNetworkAccount::with_allowlist(allowed_script_roots) - .map_err(|err| { - AccountError::other(format!("invalid network account allowlist: {err}")) - })? - .into(); - Ok(Self { - inner: component, - scheme: AccountAuthScheme::NetworkAccount, - }) - } - - /// Builds an [`AuthMultisig`] component from the given configuration. - pub fn multisig(config: AuthMultisigConfig) -> Result { - let component = AuthMultisig::new(config)?.into(); - Ok(Self { - inner: component, - scheme: AccountAuthScheme::Multisig, - }) - } - - /// Builds an [`AuthGuardedMultisig`] component from the given configuration. - pub fn guarded_multisig(config: AuthGuardedMultisigConfig) -> Result { - let component = AuthGuardedMultisig::new(config)?.into(); - Ok(Self { - inner: component, - scheme: AccountAuthScheme::GuardedMultisig, - }) - } - - /// Builds an [`AuthMultisigSmart`] component from the given configuration. - pub fn multisig_smart(config: AuthMultisigSmartConfig) -> Result { - let component = AuthMultisigSmart::new(config)?.into(); - Ok(Self { - inner: component, - scheme: AccountAuthScheme::MultisigSmart, - }) - } - - /// Returns the [`AccountAuthScheme`] tag identifying which standard auth scheme this - /// component wraps. Returns [`AccountAuthScheme::Custom`] for components built via - /// [`Self::custom`]. - pub fn scheme(&self) -> AccountAuthScheme { - self.scheme - } - - /// Returns a reference to the underlying [`AccountComponent`]. - pub fn as_inner(&self) -> &AccountComponent { - &self.inner - } - - /// Consumes `self` and returns the underlying [`AccountComponent`]. - pub fn into_inner(self) -> AccountComponent { - self.inner - } -} - -impl From for AccountComponent { - fn from(value: AccountAuthComponent) -> Self { - value.into_inner() - } -} - -impl From for AccountAuthComponent { - fn from(value: NoAuth) -> Self { - Self { - inner: value.into(), - scheme: AccountAuthScheme::NoAuth, - } - } -} - -impl From for AccountAuthComponent { - fn from(value: AuthSingleSig) -> Self { - Self { - inner: value.into(), - scheme: AccountAuthScheme::SingleSig, - } - } -} - -impl From for AccountAuthComponent { - fn from(value: AuthSingleSigAcl) -> Self { - Self { - inner: value.into(), - scheme: AccountAuthScheme::SingleSigAcl, - } - } -} - -impl From for AccountAuthComponent { - fn from(value: AuthNetworkAccount) -> Self { - Self { - inner: value.into(), - scheme: AccountAuthScheme::NetworkAccount, - } - } -} - -impl From for AccountAuthComponent { - fn from(value: AuthMultisig) -> Self { - Self { - inner: value.into(), - scheme: AccountAuthScheme::Multisig, - } - } -} - -impl From for AccountAuthComponent { - fn from(value: AuthGuardedMultisig) -> Self { - Self { - inner: value.into(), - scheme: AccountAuthScheme::GuardedMultisig, - } - } -} - -impl From for AccountAuthComponent { - fn from(value: AuthMultisigSmart) -> Self { - Self { - inner: value.into(), - scheme: AccountAuthScheme::MultisigSmart, - } - } -} diff --git a/crates/miden-standards/src/account/auth/mod.rs b/crates/miden-standards/src/account/auth/mod.rs index 2be83ed73f..e6caf01607 100644 --- a/crates/miden-standards/src/account/auth/mod.rs +++ b/crates/miden-standards/src/account/auth/mod.rs @@ -1,6 +1,3 @@ -mod account_auth_component; -pub use account_auth_component::{AccountAuthComponent, AccountAuthScheme}; - mod no_auth; pub use no_auth::NoAuth; diff --git a/crates/miden-standards/src/account/faucets/fungible/mod.rs b/crates/miden-standards/src/account/faucets/fungible/mod.rs index ef7f016fce..b246cd0366 100644 --- a/crates/miden-standards/src/account/faucets/fungible/mod.rs +++ b/crates/miden-standards/src/account/faucets/fungible/mod.rs @@ -34,7 +34,7 @@ use super::{ }; use crate::account::access::{AccessControl, Authority, PausableManager}; use crate::account::account_component_code; -use crate::account::auth::{AccountAuthComponent, AccountAuthScheme, AuthSingleSigAclConfig}; +use crate::account::auth::{AuthNetworkAccount, AuthSingleSigAcl, AuthSingleSigAclConfig}; use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; use crate::account::policies::TokenPolicyManager; use crate::procedure_root; @@ -594,150 +594,58 @@ fn all_authority_gated_setter_roots() -> Vec { /// `pause` / `unpause` admin procedures (gated by the [`Authority::AuthControlled`] component /// installed by this factory). /// -/// The caller provides any [`AccountAuthComponent`]; the factory validates the scheme: -/// -/// - [`AccountAuthScheme::SingleSig`]: rejected — under AuthControlled, every authority-gated -/// setter must be tracked by the auth component. Construct an ACL via -/// [`AccountAuthComponent::single_sig_acl`] instead (the trigger list is irrelevant; the factory -/// rebuilds it). -/// - [`AccountAuthScheme::SingleSigAcl`]: the factory installs the provided component as-is; -/// callers should already have populated the trigger list with every authority-gated setter root. -/// [`user_faucet_single_sig_acl`] is the recommended convenience constructor. -/// - [`AccountAuthScheme::NoAuth`]: rejected via -/// [`FungibleFaucetError::IncompatibleAuthControlledAuth`] — would leave setters permissionless. -/// - [`AccountAuthScheme::NetworkAccount`]: rejected — network-style faucets must use -/// [`create_network_fungible_faucet`] with [`AccessControl::Ownable2Step`] or -/// [`AccessControl::Rbac`]. -/// - [`AccountAuthScheme::Custom`]: accepted. The caller is responsible for ensuring the custom -/// auth component authenticates every authority-gated setter root. +/// Caller passes a fully-configured [`AuthSingleSigAcl`] — its trigger procedure list must +/// cover every authority-gated setter. Use [`user_faucet_single_sig_acl`] to construct one +/// with the canonical trigger list. pub fn create_user_fungible_faucet( init_seed: [u8; 32], faucet: FungibleFaucet, - auth_component: AccountAuthComponent, + auth_component: AuthSingleSigAcl, token_policy_manager: TokenPolicyManager, account_type: AccountType, ) -> Result { - let auth_component = validate_user_faucet_auth(auth_component)?; - - let account = AccountBuilder::new(init_seed) + AccountBuilder::new(init_seed) .account_type(account_type) - .with_auth_component(auth_component.into_inner()) + .with_auth_component(auth_component) .with_component(faucet) .with_component(Authority::AuthControlled) .with_components(token_policy_manager) + .with_component(PausableManager) .build() - .map_err(FungibleFaucetError::AccountError)?; - - Ok(account) + .map_err(FungibleFaucetError::AccountError) } /// Creates a new **network-style** fungible faucet. Setter gating is enforced in-procedure by /// the owner / role check installed via `access_control` ([`AccessControl::Ownable2Step`] or /// [`AccessControl::Rbac`]). The auth component only governs the faucet's own transaction /// authentication. -/// -/// Validates the auth scheme: -/// -/// - [`AccountAuthScheme::NetworkAccount`], [`AccountAuthScheme::NoAuth`], -/// [`AccountAuthScheme::Custom`]: accepted. -/// - [`AccountAuthScheme::SingleSig`], [`AccountAuthScheme::SingleSigAcl`]: rejected via -/// [`FungibleFaucetError::UnsupportedAccessControlAuthCombination`] — SingleSig is for -/// user-account faucets (see [`create_user_fungible_faucet`]). pub fn create_network_fungible_faucet( init_seed: [u8; 32], faucet: FungibleFaucet, access_control: AccessControl, - auth_component: AccountAuthComponent, + auth_component: AuthNetworkAccount, token_policy_manager: TokenPolicyManager, account_type: AccountType, ) -> Result { - let auth_component = validate_network_faucet_auth(auth_component)?; - - let account = AccountBuilder::new(init_seed) + AccountBuilder::new(init_seed) .account_type(account_type) - .with_auth_component(auth_component.into_inner()) + .with_auth_component(auth_component) .with_component(faucet) .with_components(access_control) .with_components(token_policy_manager) .with_component(PausableManager) .build() - .map_err(FungibleFaucetError::AccountError)?; - - Ok(account) + .map_err(FungibleFaucetError::AccountError) } -/// Validates and (when needed) rewrites the auth component for a user-account faucet. -/// -/// For [`AccountAuthScheme::SingleSigAcl`] the configured ACL is left intact — the caller is -/// trusted to have included all authority-gated setter roots in the trigger list. Use -/// [`all_authority_gated_setter_roots`] when constructing the ACL configuration. -fn validate_user_faucet_auth( - auth_component: AccountAuthComponent, -) -> Result { - match auth_component.scheme() { - AccountAuthScheme::SingleSigAcl => Ok(auth_component), - AccountAuthScheme::Custom => Ok(auth_component), - AccountAuthScheme::SingleSig => { - Err(FungibleFaucetError::UnsupportedAccessControlAuthCombination( - "plain SingleSig cannot gate authority-protected setters under AuthControlled; \ - use AccountAuthComponent::single_sig_acl with all_authority_gated_setter_roots() \ - as the trigger list." - .into(), - )) - }, - AccountAuthScheme::NoAuth => Err(FungibleFaucetError::IncompatibleAuthControlledAuth( - "NoAuth cannot authenticate authority-gated setters".into(), - )), - AccountAuthScheme::NetworkAccount => { - Err(FungibleFaucetError::UnsupportedAccessControlAuthCombination( - "NetworkAccount is only supported with create_network_fungible_faucet \ - (AccessControl::Ownable2Step / Rbac)" - .into(), - )) - }, - AccountAuthScheme::Multisig - | AccountAuthScheme::GuardedMultisig - | AccountAuthScheme::MultisigSmart => Err(FungibleFaucetError::UnsupportedAuthMethod( - "fungible faucets do not support Multisig authentication".into(), - )), - } -} - -/// Validates the auth component for a network-style faucet. SingleSig variants are rejected; -/// network or no-auth (and custom) are allowed. -fn validate_network_faucet_auth( - auth_component: AccountAuthComponent, -) -> Result { - match auth_component.scheme() { - AccountAuthScheme::NetworkAccount - | AccountAuthScheme::NoAuth - | AccountAuthScheme::Custom => Ok(auth_component), - AccountAuthScheme::SingleSig | AccountAuthScheme::SingleSigAcl => { - Err(FungibleFaucetError::UnsupportedAccessControlAuthCombination( - "SingleSig is only supported with create_user_fungible_faucet \ - (Authority::AuthControlled)" - .into(), - )) - }, - AccountAuthScheme::Multisig - | AccountAuthScheme::GuardedMultisig - | AccountAuthScheme::MultisigSmart => Err(FungibleFaucetError::UnsupportedAuthMethod( - "fungible faucets do not support Multisig authentication".into(), - )), - } -} - -/// Convenience constructor for the typical user-account faucet auth component: -/// [`AccountAuthComponent::single_sig_acl`] with the trigger procedure list covering every -/// authority-gated setter and `allow_unauthorized_input_notes=true`. -/// -/// Use this when calling [`create_user_fungible_faucet`] with `AuthScheme::Falcon512Poseidon2` -/// or `AuthScheme::EcdsaK256Keccak` to ensure every authority-gated setter forces a signature. +/// Convenience constructor for the typical user-account faucet auth component: an +/// [`AuthSingleSigAcl`] with the trigger procedure list covering every authority-gated setter +/// and `allow_unauthorized_input_notes=true`. pub fn user_faucet_single_sig_acl( pub_key: miden_protocol::account::auth::PublicKeyCommitment, scheme: miden_protocol::account::auth::AuthScheme, -) -> Result { - AccountAuthComponent::single_sig_acl( +) -> Result { + AuthSingleSigAcl::new( pub_key, scheme, AuthSingleSigAclConfig::new() diff --git a/crates/miden-standards/src/account/faucets/fungible/tests.rs b/crates/miden-standards/src/account/faucets/fungible/tests.rs index e0bdfe91c2..1d707cdb24 100644 --- a/crates/miden-standards/src/account/faucets/fungible/tests.rs +++ b/crates/miden-standards/src/account/faucets/fungible/tests.rs @@ -4,6 +4,7 @@ use assert_matches::assert_matches; use miden_protocol::account::auth::{AuthScheme, PublicKeyCommitment}; use miden_protocol::account::{AccountBuilder, AccountType}; use miden_protocol::asset::{AssetAmount, TokenSymbol}; +use miden_protocol::note::NoteScriptRoot; use miden_protocol::{Felt, Word}; use super::{ @@ -13,7 +14,7 @@ use super::{ user_faucet_single_sig_acl, }; use crate::account::access::{AccessControl, PausableManager}; -use crate::account::auth::{AccountAuthComponent, AuthSingleSig, AuthSingleSigAcl}; +use crate::account::auth::{AuthNetworkAccount, AuthSingleSig, AuthSingleSigAcl}; use crate::account::faucets::{Description, FungibleFaucetError, TokenMetadata, TokenName}; use crate::account::policies::{ BurnPolicyConfig, @@ -143,61 +144,11 @@ fn user_fungible_faucet_with_single_sig_acl() { let _faucet_component = FungibleFaucet::try_from(faucet_account.clone()).unwrap(); } -/// `create_user_fungible_faucet` with `NoAuth` must be rejected: the auth component is the -/// sole gate for authority-protected setters. +/// `create_network_fungible_faucet` with `Ownable2Step + AuthNetworkAccount` builds a valid +/// account. The auth component governs the faucet's own tx authentication; the setter gate is +/// enforced in-procedure by `assert_sender_is_owner`. #[test] -fn user_fungible_faucet_rejects_no_auth() { - let err = create_user_fungible_faucet( - [7u8; 32], - sample_faucet(), - AccountAuthComponent::no_auth(), - allow_all_policy_manager(), - AccountType::Private, - ) - .expect_err("user faucet with NoAuth should be rejected"); - assert_matches!(err, FungibleFaucetError::IncompatibleAuthControlledAuth(_)); -} - -/// `create_user_fungible_faucet` with a plain `SingleSig` (no ACL) must be rejected: the -/// trigger list would be empty, leaving authority-gated setters permissionless. Callers must -/// use `user_faucet_single_sig_acl`. -#[test] -fn user_fungible_faucet_rejects_plain_single_sig() { - let pub_key_word = Word::new([Felt::ONE; 4]); - let err = create_user_fungible_faucet( - [7u8; 32], - sample_faucet(), - AccountAuthComponent::single_sig(pub_key_word.into(), AuthScheme::Falcon512Poseidon2), - allow_all_policy_manager(), - AccountType::Private, - ) - .expect_err("user faucet with plain SingleSig should be rejected"); - assert_matches!(err, FungibleFaucetError::UnsupportedAccessControlAuthCombination(_)); -} - -/// `create_user_fungible_faucet` with NetworkAccount must be rejected: network-style auth -/// belongs with `create_network_fungible_faucet`. -#[test] -fn user_fungible_faucet_rejects_network_account() { - let allowlist: BTreeSet = - [miden_protocol::note::NoteScriptRoot::from_raw(Word::new([Felt::ONE; 4]))] - .into_iter() - .collect(); - let err = create_user_fungible_faucet( - [7u8; 32], - sample_faucet(), - AccountAuthComponent::network_account(allowlist).unwrap(), - allow_all_policy_manager(), - AccountType::Private, - ) - .expect_err("user faucet with NetworkAccount should be rejected"); - assert_matches!(err, FungibleFaucetError::UnsupportedAccessControlAuthCombination(_)); -} - -/// `create_network_fungible_faucet` with `Ownable2Step + NoAuth` is a valid configuration: the -/// setter gate is enforced in-procedure (`assert_sender_is_owner`). -#[test] -fn network_fungible_faucet_ownable2step_with_no_auth_is_accepted() { +fn network_fungible_faucet_with_ownable2step() { use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; let owner = miden_protocol::account::AccountId::try_from( @@ -205,41 +156,18 @@ fn network_fungible_faucet_ownable2step_with_no_auth_is_accepted() { ) .unwrap(); - let _account = create_network_fungible_faucet( - [7u8; 32], - sample_faucet(), - AccessControl::Ownable2Step { owner }, - AccountAuthComponent::no_auth(), - allow_all_policy_manager(), - AccountType::Public, - ) - .expect("Ownable2Step+NoAuth should be accepted"); -} - -/// `create_network_fungible_faucet` with SingleSig must be rejected: SingleSig belongs with -/// the user-account faucet factory. -#[test] -fn network_fungible_faucet_rejects_single_sig() { - use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; - - let owner = miden_protocol::account::AccountId::try_from( - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, - ) - .unwrap(); + let allowlist: BTreeSet = + [NoteScriptRoot::from_raw(Word::new([Felt::ONE; 4]))].into_iter().collect(); - let err = create_network_fungible_faucet( + let _account = create_network_fungible_faucet( [7u8; 32], sample_faucet(), AccessControl::Ownable2Step { owner }, - AccountAuthComponent::single_sig( - Word::new([Felt::ONE; 4]).into(), - AuthScheme::Falcon512Poseidon2, - ), + AuthNetworkAccount::with_allowlist(allowlist).unwrap(), allow_all_policy_manager(), AccountType::Public, ) - .expect_err("network faucet with SingleSig should be rejected"); - assert_matches!(err, FungibleFaucetError::UnsupportedAccessControlAuthCombination(_)); + .expect("Ownable2Step+AuthNetworkAccount should be accepted"); } #[test] diff --git a/crates/miden-standards/src/account/faucets/mod.rs b/crates/miden-standards/src/account/faucets/mod.rs index 047464de47..edf06bfa5d 100644 --- a/crates/miden-standards/src/account/faucets/mod.rs +++ b/crates/miden-standards/src/account/faucets/mod.rs @@ -1,5 +1,3 @@ -use alloc::string::String; - use miden_protocol::account::StorageSlotName; use miden_protocol::errors::{AccountError, TokenSymbolError}; use thiserror::Error; @@ -63,12 +61,6 @@ pub enum FungibleFaucetError { "account interface does not have the procedures of the basic fungible faucet component" )] MissingFungibleFaucetInterface, - #[error("unsupported authentication method: {0}")] - UnsupportedAuthMethod(String), - #[error("AuthControlled is incompatible with the chosen auth method: {0}")] - IncompatibleAuthControlledAuth(String), - #[error("unsupported combination of access control and auth method: {0}")] - UnsupportedAccessControlAuthCombination(String), #[error("account creation failed")] AccountError(#[source] AccountError), #[error("account is not a fungible faucet account")] diff --git a/crates/miden-standards/src/account/interface/component.rs b/crates/miden-standards/src/account/interface/component.rs index d4bf62ae48..5ce077a98e 100644 --- a/crates/miden-standards/src/account/interface/component.rs +++ b/crates/miden-standards/src/account/interface/component.rs @@ -5,7 +5,6 @@ use miden_protocol::Felt; use miden_protocol::account::{AccountId, AccountProcedureRoot}; use miden_protocol::note::PartialNote; -use crate::account::auth::AccountAuthScheme; use crate::account::interface::AccountInterfaceError; // ACCOUNT COMPONENT INTERFACE @@ -112,25 +111,6 @@ impl AccountComponentInterface { ) } - /// Returns the [`AccountAuthScheme`] tag for this component interface, if it is an auth - /// component. Returns [`None`] for non-auth components. - pub fn auth_scheme(&self) -> Option { - match self { - AccountComponentInterface::AuthSingleSig => Some(AccountAuthScheme::SingleSig), - AccountComponentInterface::AuthSingleSigAcl => Some(AccountAuthScheme::SingleSigAcl), - AccountComponentInterface::AuthMultisig => Some(AccountAuthScheme::Multisig), - AccountComponentInterface::AuthMultisigSmart => Some(AccountAuthScheme::MultisigSmart), - AccountComponentInterface::AuthGuardedMultisig => { - Some(AccountAuthScheme::GuardedMultisig) - }, - AccountComponentInterface::AuthNoAuth => Some(AccountAuthScheme::NoAuth), - AccountComponentInterface::AuthNetworkAccount => { - Some(AccountAuthScheme::NetworkAccount) - }, - _ => None, - } - } - /// Generates a body for the note creation of the `send_note` transaction script. The resulting /// code could use different procedures for note creation, which depends on the used interface. /// diff --git a/crates/miden-standards/src/account/interface/extension.rs b/crates/miden-standards/src/account/interface/extension.rs index eaa7c31604..aab4ea694e 100644 --- a/crates/miden-standards/src/account/interface/extension.rs +++ b/crates/miden-standards/src/account/interface/extension.rs @@ -3,7 +3,6 @@ use alloc::vec::Vec; use miden_protocol::account::{Account, AccountCode, AccountId, AccountProcedureRoot}; -use crate::account::auth::AccountAuthScheme; use crate::account::components::StandardAccountComponent; use crate::account::interface::{AccountComponentInterface, AccountInterface}; @@ -12,27 +11,22 @@ use crate::account::interface::{AccountComponentInterface, AccountInterface}; /// An extension for [`AccountInterface`] that allows instantiation from higher-level types. pub trait AccountInterfaceExt { - /// Creates a new [`AccountInterface`] instance from the provided account ID, authentication - /// scheme tags and account code. - fn from_code(account_id: AccountId, auth: Vec, code: &AccountCode) -> Self; + /// Creates a new [`AccountInterface`] instance from the provided account ID and account code. + fn from_code(account_id: AccountId, code: &AccountCode) -> Self; /// Creates a new [`AccountInterface`] instance from the provided [`Account`]. fn from_account(account: &Account) -> Self; } impl AccountInterfaceExt for AccountInterface { - fn from_code(account_id: AccountId, auth: Vec, code: &AccountCode) -> Self { + fn from_code(account_id: AccountId, code: &AccountCode) -> Self { let components = AccountComponentInterface::from_procedures(code.procedures()); - - Self::new(account_id, auth, components) + Self::new(account_id, components) } fn from_account(account: &Account) -> Self { let components = AccountComponentInterface::from_procedures(account.code().procedures()); - let auth: Vec = - components.iter().filter_map(|c| c.auth_scheme()).collect(); - - Self::new(account.id(), auth, components) + Self::new(account.id(), components) } } diff --git a/crates/miden-standards/src/account/interface/mod.rs b/crates/miden-standards/src/account/interface/mod.rs index 44f557118a..60358cca27 100644 --- a/crates/miden-standards/src/account/interface/mod.rs +++ b/crates/miden-standards/src/account/interface/mod.rs @@ -6,7 +6,6 @@ use miden_protocol::note::PartialNote; use miden_protocol::transaction::TransactionScript; use thiserror::Error; -use crate::account::auth::AccountAuthScheme; use crate::code_builder::CodeBuilder; use crate::errors::CodeBuilderError; @@ -25,7 +24,6 @@ pub use extension::{AccountComponentInterfaceExt, AccountInterfaceExt}; /// An [`AccountInterface`] describes the exported, callable procedures of an account. pub struct AccountInterface { account_id: AccountId, - auth: Vec, components: Vec, } @@ -35,14 +33,10 @@ impl AccountInterface { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Creates a new [`AccountInterface`] instance from the provided account ID, authentication - /// schemes and account component interfaces. - pub fn new( - account_id: AccountId, - auth: Vec, - components: Vec, - ) -> Self { - Self { account_id, auth, components } + /// Creates a new [`AccountInterface`] instance from the provided account ID and account + /// component interfaces. + pub fn new(account_id: AccountId, components: Vec) -> Self { + Self { account_id, components } } /// Returns `true` if the account installs an [`AccountComponentInterface::Ownable2Step`] @@ -70,16 +64,15 @@ impl AccountInterface { self.account_id.is_public() } - /// Returns a reference to the vector of [`AccountAuthScheme`] tags identifying each - /// authentication component installed on the account. - pub fn auth(&self) -> &Vec { - &self.auth - } - /// Returns a reference to the set of used component interfaces. pub fn components(&self) -> &Vec { &self.components } + + /// Returns an iterator over the auth components installed on this account. + pub fn auth_components(&self) -> impl Iterator { + self.components.iter().filter(|c| c.is_auth_component()) + } } // ------------------------------------------------------------------------------------------------ diff --git a/crates/miden-standards/src/account/interface/test.rs b/crates/miden-standards/src/account/interface/test.rs index 32b7188e9f..0a3c3f5dab 100644 --- a/crates/miden-standards/src/account/interface/test.rs +++ b/crates/miden-standards/src/account/interface/test.rs @@ -8,13 +8,7 @@ use miden_protocol::errors::NoteError; use miden_protocol::note::{NoteAttachments, NoteType}; use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE; -use crate::account::auth::{ - AccountAuthScheme, - AuthMultisig, - AuthMultisigConfig, - AuthSingleSig, - NoAuth, -}; +use crate::account::auth::{AuthMultisig, AuthMultisigConfig, AuthSingleSig, NoAuth}; use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; use crate::account::wallets::BasicWallet; use crate::note::SwapNote; @@ -43,47 +37,17 @@ fn test_required_asset_same_as_offered() { // HELPERS // ================================================================================================ -/// Helper function to create a mock auth component for testing fn get_mock_falcon_auth_component() -> AuthSingleSig { let mock_word = Word::from([0, 1, 2, 3u32]); let mock_public_key = PublicKeyCommitment::from(mock_word); AuthSingleSig::new(mock_public_key, auth::AuthScheme::Falcon512Poseidon2) } -/// Helper function to create a mock Ecdsa auth component for testing -fn get_mock_ecdsa_auth_component() -> AuthSingleSig { - let mock_word = Word::from([0, 1, 2, 3u32]); - let mock_public_key = PublicKeyCommitment::from(mock_word); - AuthSingleSig::new(mock_public_key, auth::AuthScheme::EcdsaK256Keccak) -} - -// GET AUTH SCHEME TESTS +// AUTH COMPONENT IDENTIFICATION TESTS // ================================================================================================ #[test] -fn test_get_auth_scheme_ecdsa_k256_keccak() { - let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); - let wallet_account = AccountBuilder::new(mock_seed) - .with_auth_component(get_mock_ecdsa_auth_component()) - .with_component(BasicWallet) - .build_existing() - .expect("failed to create wallet account"); - - let wallet_account_interface = AccountInterface::from_account(&wallet_account); - - // Find the EcdsaK256Keccak component interface - let ecdsa_k256_keccak_component = wallet_account_interface - .components() - .iter() - .find(|component| matches!(component, AccountComponentInterface::AuthSingleSig)) - .expect("should have EcdsaK256Keccak component"); - - // Test auth_scheme classifier - assert_eq!(ecdsa_k256_keccak_component.auth_scheme(), Some(AccountAuthScheme::SingleSig)); -} - -#[test] -fn test_get_auth_scheme_falcon512_poseidon2() { +fn test_account_interface_identifies_single_sig_auth() { let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); let wallet_account = AccountBuilder::new(mock_seed) .with_auth_component(get_mock_falcon_auth_component()) @@ -93,19 +57,13 @@ fn test_get_auth_scheme_falcon512_poseidon2() { let wallet_account_interface = AccountInterface::from_account(&wallet_account); - // Find the single sig component interface - let rpo_falcon_component = wallet_account_interface - .components() - .iter() - .find(|component| matches!(component, AccountComponentInterface::AuthSingleSig)) - .expect("should have single sig component"); - - // Test auth_scheme classifier - assert_eq!(rpo_falcon_component.auth_scheme(), Some(AccountAuthScheme::SingleSig)); + let auth_components: alloc::vec::Vec<_> = wallet_account_interface.auth_components().collect(); + assert_eq!(auth_components.len(), 1); + assert!(matches!(auth_components[0], AccountComponentInterface::AuthSingleSig)); } #[test] -fn test_get_auth_scheme_no_auth() { +fn test_account_interface_identifies_no_auth() { let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); let no_auth_account = AccountBuilder::new(mock_seed) .with_auth_component(NoAuth) @@ -115,88 +73,16 @@ fn test_get_auth_scheme_no_auth() { let no_auth_account_interface = AccountInterface::from_account(&no_auth_account); - // Find the NoAuth component interface - let no_auth_component = no_auth_account_interface - .components() - .iter() - .find(|component| matches!(component, AccountComponentInterface::AuthNoAuth)) - .expect("should have NoAuth component"); - - // Test auth_scheme classifier - assert_eq!(no_auth_component.auth_scheme(), Some(AccountAuthScheme::NoAuth)); + let auth_components: alloc::vec::Vec<_> = no_auth_account_interface.auth_components().collect(); + assert_eq!(auth_components.len(), 1); + assert!(matches!(auth_components[0], AccountComponentInterface::AuthNoAuth)); } -/// Test that non-auth components return None #[test] -fn test_get_auth_scheme_non_auth_component() { - let basic_wallet_component = AccountComponentInterface::BasicWallet; - let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); - let wallet_account = AccountBuilder::new(mock_seed) - .with_auth_component(get_mock_falcon_auth_component()) - .with_component(BasicWallet) - .build_existing() - .expect("failed to create wallet account"); - - assert!(basic_wallet_component.auth_scheme().is_none()); - let _ = wallet_account.storage(); -} - -/// Test that the From<&Account> implementation correctly uses get_auth_scheme -#[test] -fn test_account_interface_from_account_uses_get_auth_scheme() { - let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); - let wallet_account = AccountBuilder::new(mock_seed) - .with_auth_component(get_mock_falcon_auth_component()) - .with_component(BasicWallet) - .build_existing() - .expect("failed to create wallet account"); - - let wallet_account_interface = AccountInterface::from_account(&wallet_account); - - // Should have exactly one auth scheme - assert_eq!(wallet_account_interface.auth().len(), 1); - assert_eq!(wallet_account_interface.auth()[0], AccountAuthScheme::SingleSig); - - // Test with NoAuth - let no_auth_account = AccountBuilder::new(mock_seed) - .with_auth_component(NoAuth) - .with_component(BasicWallet) - .build_existing() - .expect("failed to create no-auth account"); - - let no_auth_account_interface = AccountInterface::from_account(&no_auth_account); - - assert_eq!(no_auth_account_interface.auth().len(), 1); - assert_eq!(no_auth_account_interface.auth()[0], AccountAuthScheme::NoAuth); -} - -/// Test AccountInterface.get_auth_scheme() method with Falcon512Poseidon2 and NoAuth -#[test] -fn test_account_interface_get_auth_scheme() { - let mock_seed = Word::from([0, 1, 2, 3u32]).as_bytes(); - let wallet_account = AccountBuilder::new(mock_seed) - .with_auth_component(get_mock_falcon_auth_component()) - .with_component(BasicWallet) - .build_existing() - .expect("failed to create wallet account"); - - let wallet_account_interface = AccountInterface::from_account(&wallet_account); - - // Test that auth() method provides the authentication scheme tags - assert_eq!(wallet_account_interface.auth().len(), 1); - assert_eq!(wallet_account_interface.auth()[0], AccountAuthScheme::SingleSig); - - // Test AccountInterface auth() with NoAuth - let no_auth_account = AccountBuilder::new(mock_seed) - .with_auth_component(NoAuth) - .with_component(BasicWallet) - .build_existing() - .expect("failed to create no-auth account"); - - let no_auth_account_interface = AccountInterface::from_account(&no_auth_account); - - assert_eq!(no_auth_account_interface.auth().len(), 1); - assert_eq!(no_auth_account_interface.auth()[0], AccountAuthScheme::NoAuth); +fn test_basic_wallet_is_not_an_auth_component() { + assert!(!AccountComponentInterface::BasicWallet.is_auth_component()); + assert!(AccountComponentInterface::AuthSingleSig.is_auth_component()); + assert!(AccountComponentInterface::AuthNoAuth.is_auth_component()); } #[test] diff --git a/crates/miden-standards/src/account/wallets/mod.rs b/crates/miden-standards/src/account/wallets/mod.rs index b029b131bf..09468f965d 100644 --- a/crates/miden-standards/src/account/wallets/mod.rs +++ b/crates/miden-standards/src/account/wallets/mod.rs @@ -1,5 +1,3 @@ -use alloc::string::String; - use miden_protocol::account::component::{AccountComponentCode, AccountComponentMetadata}; use miden_protocol::account::{ Account, @@ -13,7 +11,7 @@ use miden_protocol::errors::AccountError; use thiserror::Error; use crate::account::account_component_code; -use crate::account::auth::{AccountAuthComponent, AccountAuthScheme}; +use crate::account::auth::AuthSingleSig; use crate::procedure_root; // BASIC WALLET @@ -110,55 +108,30 @@ impl From for AccountComponent { /// Basic wallet related errors. #[derive(Debug, Error)] pub enum BasicWalletError { - #[error("unsupported authentication method: {0}")] - UnsupportedAuthMethod(String), #[error("account creation failed")] AccountError(#[source] AccountError), } -/// Creates a new account with the basic wallet interface and the specified authentication -/// component. +/// Creates a new account with the basic wallet interface authenticated by the provided +/// [`AuthSingleSig`] component. /// /// The basic wallet interface exposes two procedures: /// - `receive_asset`, which can be used to add an asset to the account. /// - `move_asset_to_note`, which can be used to remove the specified asset from the account and add /// it to the output note with the specified index. /// -/// All methods require authentication. The caller provides any [`AccountAuthComponent`]; this -/// factory rejects schemes that don't make sense for a basic wallet (`NoAuth` and -/// `NetworkAccount`), and accepts SingleSig, Multisig variants, and Custom components. +/// For wallets backed by other auth schemes (multisig variants), use [`AccountBuilder`] directly. pub fn create_basic_wallet( init_seed: [u8; 32], - auth_component: AccountAuthComponent, + auth_component: AuthSingleSig, account_type: AccountType, ) -> Result { - match auth_component.scheme() { - AccountAuthScheme::SingleSig - | AccountAuthScheme::SingleSigAcl - | AccountAuthScheme::Multisig - | AccountAuthScheme::GuardedMultisig - | AccountAuthScheme::MultisigSmart - | AccountAuthScheme::Custom => {}, - AccountAuthScheme::NoAuth => { - return Err(BasicWalletError::UnsupportedAuthMethod( - "basic wallets cannot be created with NoAuth authentication method".into(), - )); - }, - AccountAuthScheme::NetworkAccount => { - return Err(BasicWalletError::UnsupportedAuthMethod( - "basic wallets cannot be created with NetworkAccount authentication method".into(), - )); - }, - } - - let account = AccountBuilder::new(init_seed) + AccountBuilder::new(init_seed) .account_type(account_type) - .with_auth_component(auth_component.into_inner()) + .with_auth_component(auth_component) .with_component(BasicWallet) .build() - .map_err(BasicWalletError::AccountError)?; - - Ok(account) + .map_err(BasicWalletError::AccountError) } // TESTS @@ -170,8 +143,7 @@ mod tests { use miden_protocol::utils::serde::{Deserializable, Serializable}; use miden_protocol::{ONE, Word}; - use super::{Account, AccountType, create_basic_wallet}; - use crate::account::auth::AccountAuthComponent; + use super::{Account, AccountType, AuthSingleSig, create_basic_wallet}; use crate::account::wallets::BasicWallet; #[test] @@ -180,7 +152,7 @@ mod tests { let auth_scheme = auth::AuthScheme::Falcon512Poseidon2; let wallet = create_basic_wallet( [1; 32], - AccountAuthComponent::single_sig(pub_key, auth_scheme), + AuthSingleSig::new(pub_key, auth_scheme), AccountType::Public, ); @@ -195,7 +167,7 @@ mod tests { let auth_scheme = auth::AuthScheme::EcdsaK256Keccak; let wallet = create_basic_wallet( [1; 32], - AccountAuthComponent::single_sig(pub_key, auth_scheme), + AuthSingleSig::new(pub_key, auth_scheme), AccountType::Public, ) .unwrap(); diff --git a/crates/miden-standards/src/testing/account_interface.rs b/crates/miden-standards/src/testing/account_interface.rs index edbc8a9ba6..49556f5d39 100644 --- a/crates/miden-standards/src/testing/account_interface.rs +++ b/crates/miden-standards/src/testing/account_interface.rs @@ -4,7 +4,6 @@ use miden_protocol::Word; use miden_protocol::account::Account; use crate::account::auth::{ - AccountAuthScheme, AuthGuardedMultisig, AuthMultisig, AuthMultisigSmart, @@ -21,37 +20,34 @@ pub fn get_public_keys_from_account(account: &Account) -> Vec { let mut keys = Vec::new(); for component in interface.components() { - match component.auth_scheme() { - Some(AccountAuthScheme::SingleSig) => { + match component { + AccountComponentInterface::AuthSingleSig => { if let Ok(key) = storage.get_item(AuthSingleSig::public_key_slot()) { keys.push(key); } }, - Some(AccountAuthScheme::SingleSigAcl) => { + AccountComponentInterface::AuthSingleSigAcl => { if let Ok(key) = storage.get_item(AuthSingleSigAcl::public_key_slot()) { keys.push(key); } }, - Some(AccountAuthScheme::Multisig) => { + AccountComponentInterface::AuthMultisig => { keys.extend(read_multisig_public_keys( storage, - component, AuthMultisig::threshold_config_slot(), AuthMultisig::approver_public_keys_slot(), )); }, - Some(AccountAuthScheme::GuardedMultisig) => { + AccountComponentInterface::AuthGuardedMultisig => { keys.extend(read_multisig_public_keys( storage, - component, AuthGuardedMultisig::threshold_config_slot(), AuthGuardedMultisig::approver_public_keys_slot(), )); }, - Some(AccountAuthScheme::MultisigSmart) => { + AccountComponentInterface::AuthMultisigSmart => { keys.extend(read_multisig_public_keys( storage, - component, AuthMultisigSmart::threshold_config_slot(), AuthMultisigSmart::approver_public_keys_slot(), )); @@ -64,7 +60,6 @@ pub fn get_public_keys_from_account(account: &Account) -> Vec { fn read_multisig_public_keys( storage: &miden_protocol::account::AccountStorage, - _component: &AccountComponentInterface, config_slot: &miden_protocol::account::StorageSlotName, keys_slot: &miden_protocol::account::StorageSlotName, ) -> Vec { diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index c20782e6f7..aa02f8178a 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -55,8 +55,11 @@ use miden_protocol::transaction::{ TransactionSummary, }; use miden_protocol::{Felt, Hasher, ONE, Word}; -use miden_standards::account::auth::AccountAuthScheme; -use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; +use miden_standards::account::interface::{ + AccountComponentInterface, + AccountInterface, + AccountInterfaceExt, +}; use miden_standards::account::wallets::BasicWallet; use miden_standards::code_builder::CodeBuilder; use miden_standards::note::P2idNote; @@ -555,7 +558,10 @@ async fn tx_summary_commitment_is_signed_by_falcon_auth() -> anyhow::Result<()> let summary_commitment = summary.to_commitment(); let account_interface = AccountInterface::from_account(&account); - assert_eq!(account_interface.auth().first(), Some(&AccountAuthScheme::SingleSig)); + assert!(matches!( + account_interface.auth_components().next(), + Some(AccountComponentInterface::AuthSingleSig) + )); let pub_keys = get_public_keys_from_account(&account); let pub_key = pub_keys.first().expect("expected at least one public key"); @@ -609,7 +615,10 @@ async fn tx_summary_commitment_is_signed_by_ecdsa_auth() -> anyhow::Result<()> { let summary_commitment = summary.to_commitment(); let account_interface = AccountInterface::from_account(&account); - assert_eq!(account_interface.auth().first(), Some(&AccountAuthScheme::SingleSig)); + assert!(matches!( + account_interface.auth_components().next(), + Some(AccountComponentInterface::AuthSingleSig) + )); let pub_keys = get_public_keys_from_account(&account); let pub_key = pub_keys.first().expect("expected at least one public key"); diff --git a/crates/miden-testing/tests/wallet/mod.rs b/crates/miden-testing/tests/wallet/mod.rs index 000bcbbf4c..452405f7bf 100644 --- a/crates/miden-testing/tests/wallet/mod.rs +++ b/crates/miden-testing/tests/wallet/mod.rs @@ -1,6 +1,5 @@ use miden_protocol::Word; use miden_protocol::account::auth::AuthSecretKey; -use miden_standards::account::auth::AccountAuthComponent; use miden_standards::account::wallets::create_basic_wallet; use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::SeedableRng; @@ -19,7 +18,7 @@ fn wallet_creation() { let sec_key = AuthSecretKey::new_falcon512_poseidon2_with_rng(&mut rng); let auth_scheme = auth::AuthScheme::Falcon512Poseidon2; let pub_key = sec_key.public_key().to_commitment(); - let auth_component = AccountAuthComponent::single_sig(pub_key, auth_scheme); + let auth_component = AuthSingleSig::new(pub_key, auth_scheme); // we need to use an initial seed to create the wallet account let init_seed: [u8; 32] = [ @@ -58,7 +57,7 @@ fn wallet_creation_2() { let sec_key = AuthSecretKey::new_ecdsa_k256_keccak_with_rng(&mut rng); let auth_scheme = auth::AuthScheme::EcdsaK256Keccak; let pub_key = sec_key.public_key().to_commitment(); - let auth_component = AccountAuthComponent::single_sig(pub_key, auth_scheme); + let auth_component = AuthSingleSig::new(pub_key, auth_scheme); // we need to use an initial seed to create the wallet account let init_seed: [u8; 32] = [ From 08471ba58c664d3aa14bf28a0a51e174f002c9ab Mon Sep 17 00:00:00 2001 From: onurinanc Date: Thu, 4 Jun 2026 12:15:48 +0200 Subject: [PATCH 08/12] apply suggestions --- CHANGELOG.md | 16 +--- .../src/account/faucets/fungible/mod.rs | 29 ++----- .../src/account/faucets/fungible/tests.rs | 10 +-- .../src/account/faucets/mod.rs | 3 +- .../src/account/wallets/mod.rs | 14 +-- crates/miden-standards/src/testing/faucet.rs | 24 +++++ crates/miden-standards/src/testing/mod.rs | 2 + .../src/mock_chain/chain_builder.rs | 87 ++++++------------- 8 files changed, 66 insertions(+), 119 deletions(-) create mode 100644 crates/miden-standards/src/testing/faucet.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 12b1988134..65a3cebd31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ - Clarified the transaction definition and the distinction between execution and proving on the architecture overview page ([#3015](https://github.com/0xMiden/protocol/pull/3015)). - [BREAKING] Refactored `TransferPolicy`, `MintPolicyConfig`, and `BurnPolicyConfig` from enums into structs ([#2974](https://github.com/0xMiden/protocol/pull/2974)). - Added `AccountComponent::has_procedure(root)` helper ([#2974](https://github.com/0xMiden/protocol/pull/2974)). +- [BREAKING] Removed `AuthMethod` enum. Faucet and wallet factories now take concrete auth-component types so invalid configurations are rejected ([#2944](https://github.com/0xMiden/protocol/pull/2944)). +- [BREAKING] Split `create_fungible_faucet` into `create_user_fungible_faucet` and `create_network_fungible_faucet` ([#2944](https://github.com/0xMiden/protocol/pull/2944)). ### Fixes - Fixed `update_ger` to explicitly reject duplicate GER insertions with `ERR_GER_ALREADY_REGISTERED` instead of silently accepting them ([#2983](https://github.com/0xMiden/protocol/pull/2983)). @@ -76,7 +78,6 @@ - [BREAKING] All `get_metadata` procedures (`active_note`, `input_note`, `output_note`) no longer return attachments ([#2795](https://github.com/0xMiden/protocol/pull/2795), [#2849](https://github.com/0xMiden/protocol/pull/2849)). - [BREAKING] Added `NoteScriptRoot` newtype wrapping note script roots ([#2851](https://github.com/0xMiden/protocol/pull/2851)). - Re-exported `MIN_STACK_DEPTH` from `miden-processor` ([#2856](https://github.com/0xMiden/protocol/pull/2856)). -- [BREAKING] Renamed `NoteId` to `NoteDetailsCommitment`, new `NoteId` struct now includes the NoteMetadata ([#2861](https://github.com/0xMiden/protocol/pull/2861)). - Added `metadata_into_tag` helper for extracting the tag from metadata. This should be used instead of extracting the tag manually from the header ([#2871](https://github.com/0xMiden/protocol/pull/2871)). - [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)). - Added standardized `NetworkAccountNoteAllowlist` slot for detecting network accounts ([#2883](https://github.com/0xMiden/protocol/pull/2883)). @@ -91,13 +92,6 @@ - Added `Authority` account component ([#2925](https://github.com/0xMiden/protocol/pull/2925)). - [BREAKING] `FungibleAsset::amount()` and `AssetVault::get_balance()` now return `AssetAmount` ([#2928](https://github.com/0xMiden/protocol/pull/2928)). - [BREAKING] Upgraded `miden-vm` to v0.23 and `miden-crypto` to v0.25. Notable downstream changes: dropped the immediate form of `adv_push` in kernel and standards MASM, marked cross-module-referenced MASM constants and procedures `pub`, migrated to the split `Host`/`BaseHost` trait surface, renamed `Felt::new` call sites to the preserved-behavior `Felt::new_unchecked`, switched `ecdsa_k256_keccak`/`eddsa_25519_sha512` `SecretKey` references to the new `SigningKey`/`KeyExchangeKey` types, and recomputed the kernel's `EMPTY_SMT_ROOT` constant for the Plonky3-aligned Poseidon2 and domain-separated `SmtLeaf::hash` ([#2931](https://github.com/0xMiden/protocol/pull/2931)). -- Derive `Hash` implementation for `StorageMapKey` and `StorageMapKeyHash` to allow using those values as keys in containers ([#2843](https://github.com/0xMiden/protocol/issues/2843)). -- [BREAKING] Removed `AuthMethod` enum and `AccessControl::AuthControlled` variant. Faucet factories now take concrete auth-component types so invalid configurations are rejected at compile time. ([#2944](https://github.com/0xMiden/protocol/pull/2944)). -- [BREAKING] Split `create_fungible_faucet` into `create_user_fungible_faucet` (takes `AuthSingleSigAcl`, installs `Authority::AuthControlled` directly) and `create_network_fungible_faucet` (takes `AuthNetworkAccount` and `AccessControl::Ownable2Step | Rbac`). For any other auth scheme, use `AccountBuilder` directly. A new `user_faucet_single_sig_acl` helper builds an `AuthSingleSigAcl` with the complete authority-gated setter trigger list. ([#2944](https://github.com/0xMiden/protocol/pull/2944)). -- [BREAKING] `create_basic_wallet` now takes `AuthSingleSig` directly instead of a wrapped auth type. For multisig wallets, use `AccountBuilder` directly. ([#2944](https://github.com/0xMiden/protocol/pull/2944)). -- [BREAKING] Removed `AccountInterface::auth()` method (returning `Vec`). Auth components are now discovered via `AccountInterface::auth_components()`, which iterates `AccountComponentInterface` variants flagged by `is_auth_component()`. ([#2944](https://github.com/0xMiden/protocol/pull/2944)). -- Optimized `B2AGG` processing with selective load/save of Local Exit Tree frontier entries, halving frontier storage map syscalls ([#2752](https://github.com/0xMiden/protocol/pull/2752)). -- [BREAKING] Removed `AccountType` ([#2939](https://github.com/0xMiden/protocol/pull/2939)). - [BREAKING] Removed `AccountType` and renamed `AccountStorageMode` to `AccountType` ([#2939](https://github.com/0xMiden/protocol/pull/2939), [#2942](https://github.com/0xMiden/protocol/pull/2942)). - [BREAKING] Updated note nullifiers to include note metadata and attachments commitment ([#2953](https://github.com/0xMiden/protocol/pull/2953)). - Exposed `token_config_slot_value` on `FungibleFaucet` to allow reading the token config word directly from the account storage ([#2954](https://github.com/0xMiden/protocol/pull/2954)). @@ -105,21 +99,17 @@ ### Fixes - Fixed `LocalTransactionProver` accumulating `MastForest` entries across `prove()` calls, causing `capacity_overflow` panics in WASM environments where linear memory fragmentation prevents subsequent allocations ([#2918](https://github.com/0xMiden/protocol/pull/2918)). -- Fixed `create_fungible_faucet` leaving authority-gated setters unauthenticated under `AccessControl::AuthControlled`: the `AuthSingleSigAcl` trigger list now contains every authority-gated setter root (`set_max_supply`, `set_description`, `set_logo_uri`, `set_external_link`, `set_mint_policy`, `set_burn_policy`, `set_send_policy`, `set_receive_policy`) in addition to `mint_and_send`. Replaced with `create_user_fungible_faucet` which validates the auth component at the type level. ([#2943](https://github.com/0xMiden/protocol/issues/2943), [#2944](https://github.com/0xMiden/protocol/pull/2944), [#2958](https://github.com/0xMiden/protocol/pull/2958)). - Made deserialization of `AccountCode` more robust ([#2788](https://github.com/0xMiden/protocol/pull/2788)). - Validated `PartialBlockchain` invariants on deserialization ([#2888](https://github.com/0xMiden/protocol/pull/2888)). - Fixed `output_note::add_asset` and `output_note::set_attachment` to no longer accept invalid note indices ([#2824](https://github.com/0xMiden/protocol/pull/2824)). - Fixed auth components to use initial storage state for authentication ([#2677](https://github.com/0xMiden/protocol/issues/2677)). -- Made deserialization of `AccountCode` more robust ([#2788](https://github.com/0xMiden/protocol/pull/2788)). - [BREAKING] Replaced `NoAuth` with the new `AuthNetworkAccount` auth component on the AggLayer bridge and AggLayer faucet, closing the forged-MINT attack surface where any transaction against the bridge could emit a bridge-authored MINT note ([#2797](https://github.com/0xMiden/protocol/issues/2797), [#2818](https://github.com/0xMiden/protocol/pull/2818)). - Renamed the AggLayer faucet registry flag constant for clarity ([#2812](https://github.com/0xMiden/protocol/issues/2812)). -- Fixed `output_note::add_asset` and `output_note::set_attachment` to no longer accept invalid note indices ([#2824](https://github.com/0xMiden/protocol/pull/2824)). - [BREAKING] Keyed the AggLayer faucet token registry by `(origin_token_address, origin_network)` instead of `origin_token_address` alone, preventing same-address cross-network mint collisions on CLAIM ([#2860](https://github.com/0xMiden/protocol/pull/2860)). -- Validated `PartialBlockchain` invariants on deserialization ([#2888](https://github.com/0xMiden/protocol/pull/2888)). - Fixed `set_procedure_threshold` in the multisig auth component validating per-procedure overrides against initial `num_approvers`. - Bound MINT notes to their faucet ([#2911](https://github.com/0xMiden/protocol/pull/2911)). -- Fixed `LocalTransactionProver` accumulating `MastForest` entries across `prove()` calls, causing `capacity_overflow` panics in WASM environments where linear memory fragmentation prevents subsequent allocations ([#2918](https://github.com/0xMiden/protocol/pull/2918)). - Fixed `TokenPolicyManager::manager_storage_slots` to register the protocol-reserved asset-callback storage slots whenever any transfer policy is configured (including `TransferAllowAll`), so every minted asset carries `AssetCallbackFlag::Enabled` and future `set_send_policy` / `set_receive_policy` switches apply uniformly to the entire circulating supply ([#2946](https://github.com/0xMiden/protocol/pull/2946)). +- Fixed `create_fungible_faucet` leaving authority-gated setters unauthenticated under `AccessControl::AuthControlled`: the `AuthSingleSigAcl` trigger list now contains every authority-gated setter root (`set_max_supply`, `set_description`, `set_logo_uri`, `set_external_link`, `set_mint_policy`, `set_burn_policy`, `set_send_policy`, `set_receive_policy`) in addition to `mint_and_send` ([#2943](https://github.com/0xMiden/protocol/issues/2943), [#2958](https://github.com/0xMiden/protocol/pull/2958)). - [BREAKING] Added missing transaction `ref_block_commitment` validation in `ProposedBatch::new` ([#2971](https://github.com/0xMiden/protocol/pull/2971)). ## 0.14.6 (2026-05-09) diff --git a/crates/miden-standards/src/account/faucets/fungible/mod.rs b/crates/miden-standards/src/account/faucets/fungible/mod.rs index b246cd0366..b1604900be 100644 --- a/crates/miden-standards/src/account/faucets/fungible/mod.rs +++ b/crates/miden-standards/src/account/faucets/fungible/mod.rs @@ -34,7 +34,7 @@ use super::{ }; use crate::account::access::{AccessControl, Authority, PausableManager}; use crate::account::account_component_code; -use crate::account::auth::{AuthNetworkAccount, AuthSingleSigAcl, AuthSingleSigAclConfig}; +use crate::account::auth::{AuthNetworkAccount, AuthSingleSigAcl}; use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; use crate::account::policies::TokenPolicyManager; use crate::procedure_root; @@ -568,9 +568,9 @@ impl TryFrom<&Account> for FungibleFaucet { /// /// Under [`Authority::AuthControlled`] the auth component must authenticate calls to all of /// these procedures, otherwise the setters become permissionless. This list is the single -/// source of truth used by [`create_user_fungible_faucet`] when configuring the -/// [`AuthSingleSigAcl`][crate::account::auth::AuthSingleSigAcl] trigger procedure list. -fn all_authority_gated_setter_roots() -> Vec { +/// source of truth used when configuring an [`AuthSingleSigAcl`] trigger procedure list for a +/// user-account fungible faucet. +pub(crate) fn all_authority_gated_setter_roots() -> Vec { vec![ FungibleFaucet::mint_and_send_root(), FungibleFaucet::set_max_supply_root(), @@ -595,8 +595,8 @@ fn all_authority_gated_setter_roots() -> Vec { /// installed by this factory). /// /// Caller passes a fully-configured [`AuthSingleSigAcl`] — its trigger procedure list must -/// cover every authority-gated setter. Use [`user_faucet_single_sig_acl`] to construct one -/// with the canonical trigger list. +/// cover every authority-gated setter (see [`all_authority_gated_setter_roots`] for the +/// canonical list). pub fn create_user_fungible_faucet( init_seed: [u8; 32], faucet: FungibleFaucet, @@ -637,20 +637,3 @@ pub fn create_network_fungible_faucet( .build() .map_err(FungibleFaucetError::AccountError) } - -/// Convenience constructor for the typical user-account faucet auth component: an -/// [`AuthSingleSigAcl`] with the trigger procedure list covering every authority-gated setter -/// and `allow_unauthorized_input_notes=true`. -pub fn user_faucet_single_sig_acl( - pub_key: miden_protocol::account::auth::PublicKeyCommitment, - scheme: miden_protocol::account::auth::AuthScheme, -) -> Result { - AuthSingleSigAcl::new( - pub_key, - scheme, - AuthSingleSigAclConfig::new() - .with_auth_trigger_procedures(all_authority_gated_setter_roots()) - .with_allow_unauthorized_input_notes(true), - ) - .map_err(FungibleFaucetError::AccountError) -} diff --git a/crates/miden-standards/src/account/faucets/fungible/tests.rs b/crates/miden-standards/src/account/faucets/fungible/tests.rs index 98a698b8fd..79ae146519 100644 --- a/crates/miden-standards/src/account/faucets/fungible/tests.rs +++ b/crates/miden-standards/src/account/faucets/fungible/tests.rs @@ -7,17 +7,13 @@ use miden_protocol::asset::{AssetAmount, TokenSymbol}; use miden_protocol::note::NoteScriptRoot; use miden_protocol::{Felt, Word}; -use super::{ - FungibleFaucet, - create_network_fungible_faucet, - create_user_fungible_faucet, - user_faucet_single_sig_acl, -}; +use super::{FungibleFaucet, create_network_fungible_faucet, create_user_fungible_faucet}; use crate::account::access::{AccessControl, PausableManager}; use crate::account::auth::{AuthNetworkAccount, AuthSingleSig, AuthSingleSigAcl}; use crate::account::faucets::{Description, FungibleFaucetError, TokenMetadata, TokenName}; use crate::account::policies::{BurnPolicy, MintPolicy, TokenPolicyManager, TransferPolicy}; use crate::account::wallets::BasicWallet; +use crate::testing::faucet::user_faucet_single_sig_acl; /// Builds a minimal policy manager with AllowAll on every kind, used by the construction tests. fn allow_all_policy_manager() -> TokenPolicyManager { @@ -148,7 +144,7 @@ fn network_fungible_faucet_with_ownable2step() { .unwrap(); let allowlist: BTreeSet = - [NoteScriptRoot::from_raw(Word::new([Felt::ONE; 4]))].into_iter().collect(); + [NoteScriptRoot::from_array([0; 4])].into_iter().collect(); let _account = create_network_fungible_faucet( [7u8; 32], diff --git a/crates/miden-standards/src/account/faucets/mod.rs b/crates/miden-standards/src/account/faucets/mod.rs index edf06bfa5d..b73ec90753 100644 --- a/crates/miden-standards/src/account/faucets/mod.rs +++ b/crates/miden-standards/src/account/faucets/mod.rs @@ -5,7 +5,7 @@ use thiserror::Error; use crate::account::access::Ownable2StepError; use crate::utils::FixedWidthStringError; -mod fungible; +pub(crate) mod fungible; mod token_metadata; pub use fungible::{ @@ -13,7 +13,6 @@ pub use fungible::{ FungibleFaucetBuilder, create_network_fungible_faucet, create_user_fungible_faucet, - user_faucet_single_sig_acl, }; pub use token_metadata::{Description, ExternalLink, LogoURI, TokenMetadata, TokenName}; diff --git a/crates/miden-standards/src/account/wallets/mod.rs b/crates/miden-standards/src/account/wallets/mod.rs index 09468f965d..2e32c897b6 100644 --- a/crates/miden-standards/src/account/wallets/mod.rs +++ b/crates/miden-standards/src/account/wallets/mod.rs @@ -8,7 +8,6 @@ use miden_protocol::account::{ AccountType, }; use miden_protocol::errors::AccountError; -use thiserror::Error; use crate::account::account_component_code; use crate::account::auth::AuthSingleSig; @@ -102,16 +101,6 @@ impl From for AccountComponent { } } -// BASIC WALLET ERROR -// ================================================================================================ - -/// Basic wallet related errors. -#[derive(Debug, Error)] -pub enum BasicWalletError { - #[error("account creation failed")] - AccountError(#[source] AccountError), -} - /// Creates a new account with the basic wallet interface authenticated by the provided /// [`AuthSingleSig`] component. /// @@ -125,13 +114,12 @@ pub fn create_basic_wallet( init_seed: [u8; 32], auth_component: AuthSingleSig, account_type: AccountType, -) -> Result { +) -> Result { AccountBuilder::new(init_seed) .account_type(account_type) .with_auth_component(auth_component) .with_component(BasicWallet) .build() - .map_err(BasicWalletError::AccountError) } // TESTS diff --git a/crates/miden-standards/src/testing/faucet.rs b/crates/miden-standards/src/testing/faucet.rs new file mode 100644 index 0000000000..b886922f4e --- /dev/null +++ b/crates/miden-standards/src/testing/faucet.rs @@ -0,0 +1,24 @@ +use miden_protocol::account::auth::{AuthScheme, PublicKeyCommitment}; +use miden_protocol::errors::AccountError; + +use crate::account::auth::{AuthSingleSigAcl, AuthSingleSigAclConfig}; +use crate::account::faucets::fungible::all_authority_gated_setter_roots; + +/// Convenience constructor for the typical user-account fungible faucet auth component: an +/// [`AuthSingleSigAcl`] with the trigger procedure list covering every authority-gated setter +/// and `allow_unauthorized_input_notes=true`. +/// +/// Production callers that need a different ACL shape should construct [`AuthSingleSigAcl`] +/// directly. +pub fn user_faucet_single_sig_acl( + pub_key: PublicKeyCommitment, + scheme: AuthScheme, +) -> Result { + AuthSingleSigAcl::new( + pub_key, + scheme, + AuthSingleSigAclConfig::new() + .with_auth_trigger_procedures(all_authority_gated_setter_roots()) + .with_allow_unauthorized_input_notes(true), + ) +} diff --git a/crates/miden-standards/src/testing/mod.rs b/crates/miden-standards/src/testing/mod.rs index 01cf73f63c..bf5cbcc069 100644 --- a/crates/miden-standards/src/testing/mod.rs +++ b/crates/miden-standards/src/testing/mod.rs @@ -1,6 +1,8 @@ pub mod account_component; pub mod account_interface; +pub mod faucet; + pub mod mock_account; pub mod mock_account_code; diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 8f7e140881..2b72b85148 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -1,4 +1,4 @@ -use alloc::collections::BTreeMap; +use alloc::collections::{BTreeMap, BTreeSet}; use alloc::vec::Vec; use anyhow::Context; @@ -315,41 +315,6 @@ impl MockChainBuilder { self.add_account_from_builder(auth_method, account_builder, AccountState::Exists) } - /// Internal helper: creates a new user-account fungible faucet account (AuthControlled). - /// Installs [`Authority::AuthControlled`] directly; the auth component comes from `Auth`. - fn create_new_user_fungible_faucet( - &mut self, - auth_method: Auth, - faucet: FungibleFaucet, - account_type: AccountType, - token_policy_manager: TokenPolicyManager, - ) -> anyhow::Result { - let account_builder = AccountBuilder::new(self.rng.random()) - .account_type(account_type) - .with_component(faucet) - .with_component(Authority::AuthControlled) - .with_components(token_policy_manager); - - self.add_account_from_builder(auth_method, account_builder, AccountState::New) - } - - /// Internal helper: adds an existing user-account fungible faucet (AuthControlled). - fn add_existing_user_fungible_faucet( - &mut self, - auth_method: Auth, - faucet: FungibleFaucet, - account_type: AccountType, - token_policy_manager: TokenPolicyManager, - ) -> anyhow::Result { - let account_builder = AccountBuilder::new(self.rng.random()) - .account_type(account_type) - .with_component(faucet) - .with_component(Authority::AuthControlled) - .with_components(token_policy_manager); - - self.add_account_from_builder(auth_method, account_builder, AccountState::Exists) - } - /// Internal helper: adds an existing network-style fungible faucet (Ownable2Step / Rbac). fn add_existing_network_fungible_faucet( &mut self, @@ -369,11 +334,11 @@ impl MockChainBuilder { } /// Convenience: builds a basic auth-controlled fungible faucet from a token-symbol shorthand - /// using default decimals and `AllowAll` policies, then adds it via - /// `Self::add_existing_fungible_faucet`. + /// using default decimals and `AllowAll` policies, then adds it as an existing account with + /// [`Authority::AuthControlled`]. /// /// For full control over the faucet's metadata, decimals, and policies, construct a - /// [`FungibleFaucet`] manually and call `Self::add_existing_fungible_faucet`. + /// [`FungibleFaucet`] manually and use [`AccountBuilder`] directly. pub fn add_existing_basic_faucet( &mut self, auth_method: Auth, @@ -403,12 +368,13 @@ impl MockChainBuilder { .active_receive_policy(TransferPolicy::allow_all()) .build(); - self.add_existing_user_fungible_faucet( - auth_method, - faucet, - AccountType::Public, - token_policy_manager, - ) + let account_builder = AccountBuilder::new(self.rng.random()) + .account_type(AccountType::Public) + .with_component(faucet) + .with_component(Authority::AuthControlled) + .with_components(token_policy_manager); + + self.add_account_from_builder(auth_method, account_builder, AccountState::Exists) } /// Convenience: builds an owner-controlled (network-style) fungible faucet from a @@ -452,11 +418,10 @@ impl MockChainBuilder { .active_receive_policy(TransferPolicy::allow_all()) .build(); - let allowed_script_roots: alloc::collections::BTreeSet = - allowed_script_roots - .into_iter() - .chain([MintNote::script_root(), BurnNote::script_root()]) - .collect(); + let allowed_script_roots: BTreeSet = allowed_script_roots + .into_iter() + .chain([MintNote::script_root(), BurnNote::script_root()]) + .collect(); self.add_existing_network_fungible_faucet( Auth::NetworkAccount { allowed_script_roots }, @@ -486,11 +451,10 @@ impl MockChainBuilder { .active_receive_policy(TransferPolicy::allow_all()) .build(); - let allowed_script_roots: alloc::collections::BTreeSet = - allowed_script_roots - .into_iter() - .chain([MintNote::script_root(), BurnNote::script_root()]) - .collect(); + let allowed_script_roots: BTreeSet = allowed_script_roots + .into_iter() + .chain([MintNote::script_root(), BurnNote::script_root()]) + .collect(); self.add_existing_network_fungible_faucet( Auth::NetworkAccount { allowed_script_roots }, @@ -528,12 +492,13 @@ impl MockChainBuilder { .active_receive_policy(TransferPolicy::allow_all()) .build(); - self.create_new_user_fungible_faucet( - auth_method, - faucet, - AccountType::Public, - token_policy_manager, - ) + let account_builder = AccountBuilder::new(self.rng.random()) + .account_type(AccountType::Public) + .with_component(faucet) + .with_component(Authority::AuthControlled) + .with_components(token_policy_manager); + + self.add_account_from_builder(auth_method, account_builder, AccountState::New) } /// Creates a new public account with an [`MockAccountComponent`] and registers the From 3b9fe38cce10e44e5ac1f1847bb2c9c57765f535 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Thu, 4 Jun 2026 12:48:20 +0200 Subject: [PATCH 09/12] lint --- .../src/account/faucets/fungible/mod.rs | 27 +++---------------- .../src/account/faucets/mod.rs | 2 +- crates/miden-standards/src/testing/faucet.rs | 24 ++++++++++++++--- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/crates/miden-standards/src/account/faucets/fungible/mod.rs b/crates/miden-standards/src/account/faucets/fungible/mod.rs index b1604900be..9b591bdbba 100644 --- a/crates/miden-standards/src/account/faucets/fungible/mod.rs +++ b/crates/miden-standards/src/account/faucets/fungible/mod.rs @@ -564,28 +564,6 @@ impl TryFrom<&Account> for FungibleFaucet { // FACTORY // ================================================================================================ -/// Returns every authority-gated setter procedure root exported by a fungible faucet account. -/// -/// Under [`Authority::AuthControlled`] the auth component must authenticate calls to all of -/// these procedures, otherwise the setters become permissionless. This list is the single -/// source of truth used when configuring an [`AuthSingleSigAcl`] trigger procedure list for a -/// user-account fungible faucet. -pub(crate) fn all_authority_gated_setter_roots() -> Vec { - vec![ - FungibleFaucet::mint_and_send_root(), - FungibleFaucet::set_max_supply_root(), - FungibleFaucet::set_description_root(), - FungibleFaucet::set_logo_uri_root(), - FungibleFaucet::set_external_link_root(), - TokenPolicyManager::set_mint_policy_root(), - TokenPolicyManager::set_burn_policy_root(), - TokenPolicyManager::set_send_policy_root(), - TokenPolicyManager::set_receive_policy_root(), - PausableManager::pause_root(), - PausableManager::unpause_root(), - ] -} - /// Creates a new **user-account** fungible faucet. The account's auth component is the sole /// gate for authority-protected setters ([`Authority::AuthControlled`] is installed directly). /// @@ -595,8 +573,9 @@ pub(crate) fn all_authority_gated_setter_roots() -> Vec { /// installed by this factory). /// /// Caller passes a fully-configured [`AuthSingleSigAcl`] — its trigger procedure list must -/// cover every authority-gated setter (see [`all_authority_gated_setter_roots`] for the -/// canonical list). +/// cover every authority-gated setter on the faucet (`mint_and_send`, the metadata setters, +/// the policy setters, and `pause` / `unpause`), otherwise those procedures become +/// permissionless under [`Authority::AuthControlled`]. pub fn create_user_fungible_faucet( init_seed: [u8; 32], faucet: FungibleFaucet, diff --git a/crates/miden-standards/src/account/faucets/mod.rs b/crates/miden-standards/src/account/faucets/mod.rs index b73ec90753..d2e3aa7b1d 100644 --- a/crates/miden-standards/src/account/faucets/mod.rs +++ b/crates/miden-standards/src/account/faucets/mod.rs @@ -5,7 +5,7 @@ use thiserror::Error; use crate::account::access::Ownable2StepError; use crate::utils::FixedWidthStringError; -pub(crate) mod fungible; +mod fungible; mod token_metadata; pub use fungible::{ diff --git a/crates/miden-standards/src/testing/faucet.rs b/crates/miden-standards/src/testing/faucet.rs index b886922f4e..64014b148d 100644 --- a/crates/miden-standards/src/testing/faucet.rs +++ b/crates/miden-standards/src/testing/faucet.rs @@ -1,12 +1,17 @@ +use alloc::vec; + use miden_protocol::account::auth::{AuthScheme, PublicKeyCommitment}; use miden_protocol::errors::AccountError; +use crate::account::access::PausableManager; use crate::account::auth::{AuthSingleSigAcl, AuthSingleSigAclConfig}; -use crate::account::faucets::fungible::all_authority_gated_setter_roots; +use crate::account::faucets::FungibleFaucet; +use crate::account::policies::TokenPolicyManager; /// Convenience constructor for the typical user-account fungible faucet auth component: an /// [`AuthSingleSigAcl`] with the trigger procedure list covering every authority-gated setter -/// and `allow_unauthorized_input_notes=true`. +/// (`mint_and_send`, metadata setters, policy setters, `pause` / `unpause`) and +/// `allow_unauthorized_input_notes=true`. /// /// Production callers that need a different ACL shape should construct [`AuthSingleSigAcl`] /// directly. @@ -14,11 +19,24 @@ pub fn user_faucet_single_sig_acl( pub_key: PublicKeyCommitment, scheme: AuthScheme, ) -> Result { + let trigger_procedures = vec![ + FungibleFaucet::mint_and_send_root(), + FungibleFaucet::set_max_supply_root(), + FungibleFaucet::set_description_root(), + FungibleFaucet::set_logo_uri_root(), + FungibleFaucet::set_external_link_root(), + TokenPolicyManager::set_mint_policy_root(), + TokenPolicyManager::set_burn_policy_root(), + TokenPolicyManager::set_send_policy_root(), + TokenPolicyManager::set_receive_policy_root(), + PausableManager::pause_root(), + PausableManager::unpause_root(), + ]; AuthSingleSigAcl::new( pub_key, scheme, AuthSingleSigAclConfig::new() - .with_auth_trigger_procedures(all_authority_gated_setter_roots()) + .with_auth_trigger_procedures(trigger_procedures) .with_allow_unauthorized_input_notes(true), ) } From 17663e963cbac8002cf16e355d45b6b32382f562 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Tue, 9 Jun 2026 13:15:41 +0200 Subject: [PATCH 10/12] apply some suggestions --- CHANGELOG.md | 18 +++-- .../src/account/faucets/fungible/mod.rs | 10 ++- .../src/account/wallets/mod.rs | 78 ++++++++++++++++++- crates/miden-standards/src/testing/faucet.rs | 37 +++++---- .../src/mock_chain/chain_builder.rs | 12 ++- 5 files changed, 124 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c625721089..dc0dfd7a29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,12 @@ - Clarified the transaction definition and the distinction between execution and proving on the architecture overview page ([#3015](https://github.com/0xMiden/protocol/pull/3015)). - [BREAKING] Refactored `TransferPolicy`, `MintPolicyConfig`, and `BurnPolicyConfig` from enums into structs ([#2974](https://github.com/0xMiden/protocol/pull/2974)). - Added `AccountComponent::has_procedure(root)` helper ([#2974](https://github.com/0xMiden/protocol/pull/2974)). -- [BREAKING] Removed `AuthMethod` enum. Faucet and wallet factories now take concrete auth-component types so invalid configurations are rejected ([#2944](https://github.com/0xMiden/protocol/pull/2944)). -- [BREAKING] Split `create_fungible_faucet` into `create_user_fungible_faucet` and `create_network_fungible_faucet` ([#2944](https://github.com/0xMiden/protocol/pull/2944)). - Optimized protocol MASM stack-cleaning sequences, saving 1 cycle per occurrence across 9 single-element-extraction procedures ([#3041](https://github.com/0xMiden/protocol/pull/3041)). +- [BREAKING] Removed `AuthMethod` enum, `AccountAuthComponent` / `AccountAuthScheme`, and the `AccessControl::AuthControlled` variant. Faucet and wallet factories now take concrete auth-component types so invalid configurations are rejected at compile time ([#2944](https://github.com/0xMiden/protocol/pull/2944)). +- [BREAKING] Split `create_fungible_faucet` into `create_user_fungible_faucet(auth_component: AuthSingleSigAcl, ...)` (installs `Authority::AuthControlled` directly) and `create_network_fungible_faucet(access_control, auth_component: AuthNetworkAccount, ...)`. Other auth schemes are no longer supported through these helpers — fall back to `AccountBuilder` directly. A `user_faucet_single_sig_acl` testing helper is provided behind the `testing` feature ([#2944](https://github.com/0xMiden/protocol/pull/2944)). +- Added `create_multisig_wallet` and `create_guarded_wallet` helpers for `BasicWallet` accounts authenticated by `AuthMultisig` and `AuthGuardedMultisig` respectively ([#2944](https://github.com/0xMiden/protocol/pull/2944)). +- [BREAKING] `create_basic_wallet` now takes `AuthSingleSig` directly and returns `AccountError` instead of the removed `BasicWalletError` ([#2944](https://github.com/0xMiden/protocol/pull/2944)). +- [BREAKING] Removed `AccountInterface::auth()` and `AccountComponentInterface::auth_scheme()`. Auth components are now discovered via `AccountInterface::auth_components()`, which iterates `AccountComponentInterface` variants flagged by `is_auth_component()` ([#2944](https://github.com/0xMiden/protocol/pull/2944)). ### Fixes - Fixed `update_ger` to explicitly reject duplicate GER insertions with `ERR_GER_ALREADY_REGISTERED` instead of silently accepting them ([#2983](https://github.com/0xMiden/protocol/pull/2983)). @@ -80,6 +83,7 @@ - [BREAKING] All `get_metadata` procedures (`active_note`, `input_note`, `output_note`) no longer return attachments ([#2795](https://github.com/0xMiden/protocol/pull/2795), [#2849](https://github.com/0xMiden/protocol/pull/2849)). - [BREAKING] Added `NoteScriptRoot` newtype wrapping note script roots ([#2851](https://github.com/0xMiden/protocol/pull/2851)). - Re-exported `MIN_STACK_DEPTH` from `miden-processor` ([#2856](https://github.com/0xMiden/protocol/pull/2856)). +- [BREAKING] Renamed `NoteId` to `NoteDetailsCommitment`, new `NoteId` struct now includes the NoteMetadata ([#2861](https://github.com/0xMiden/protocol/pull/2861)). - Added `metadata_into_tag` helper for extracting the tag from metadata. This should be used instead of extracting the tag manually from the header ([#2871](https://github.com/0xMiden/protocol/pull/2871)). - [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)). - Added standardized `NetworkAccountNoteAllowlist` slot for detecting network accounts ([#2883](https://github.com/0xMiden/protocol/pull/2883)). @@ -100,18 +104,18 @@ ### Fixes -- Fixed `LocalTransactionProver` accumulating `MastForest` entries across `prove()` calls, causing `capacity_overflow` panics in WASM environments where linear memory fragmentation prevents subsequent allocations ([#2918](https://github.com/0xMiden/protocol/pull/2918)). -- Made deserialization of `AccountCode` more robust ([#2788](https://github.com/0xMiden/protocol/pull/2788)). -- Validated `PartialBlockchain` invariants on deserialization ([#2888](https://github.com/0xMiden/protocol/pull/2888)). -- Fixed `output_note::add_asset` and `output_note::set_attachment` to no longer accept invalid note indices ([#2824](https://github.com/0xMiden/protocol/pull/2824)). - Fixed auth components to use initial storage state for authentication ([#2677](https://github.com/0xMiden/protocol/issues/2677)). +- Made deserialization of `AccountCode` more robust ([#2788](https://github.com/0xMiden/protocol/pull/2788)). - [BREAKING] Replaced `NoAuth` with the new `AuthNetworkAccount` auth component on the AggLayer bridge and AggLayer faucet, closing the forged-MINT attack surface where any transaction against the bridge could emit a bridge-authored MINT note ([#2797](https://github.com/0xMiden/protocol/issues/2797), [#2818](https://github.com/0xMiden/protocol/pull/2818)). - Renamed the AggLayer faucet registry flag constant for clarity ([#2812](https://github.com/0xMiden/protocol/issues/2812)). +- Fixed `output_note::add_asset` and `output_note::set_attachment` to no longer accept invalid note indices ([#2824](https://github.com/0xMiden/protocol/pull/2824)). - [BREAKING] Keyed the AggLayer faucet token registry by `(origin_token_address, origin_network)` instead of `origin_token_address` alone, preventing same-address cross-network mint collisions on CLAIM ([#2860](https://github.com/0xMiden/protocol/pull/2860)). +- Validated `PartialBlockchain` invariants on deserialization ([#2888](https://github.com/0xMiden/protocol/pull/2888)). - Fixed `set_procedure_threshold` in the multisig auth component validating per-procedure overrides against initial `num_approvers`. - Bound MINT notes to their faucet ([#2911](https://github.com/0xMiden/protocol/pull/2911)). +- Fixed `LocalTransactionProver` accumulating `MastForest` entries across `prove()` calls, causing `capacity_overflow` panics in WASM environments where linear memory fragmentation prevents subsequent allocations ([#2918](https://github.com/0xMiden/protocol/pull/2918)). - Fixed `TokenPolicyManager::manager_storage_slots` to register the protocol-reserved asset-callback storage slots whenever any transfer policy is configured (including `TransferAllowAll`), so every minted asset carries `AssetCallbackFlag::Enabled` and future `set_send_policy` / `set_receive_policy` switches apply uniformly to the entire circulating supply ([#2946](https://github.com/0xMiden/protocol/pull/2946)). -- Fixed `create_fungible_faucet` leaving authority-gated setters unauthenticated under `AccessControl::AuthControlled`: the `AuthSingleSigAcl` trigger list now contains every authority-gated setter root (`set_max_supply`, `set_description`, `set_logo_uri`, `set_external_link`, `set_mint_policy`, `set_burn_policy`, `set_send_policy`, `set_receive_policy`) in addition to `mint_and_send` ([#2943](https://github.com/0xMiden/protocol/issues/2943), [#2958](https://github.com/0xMiden/protocol/pull/2958)). +- Fixed `create_fungible_faucet` leaving authority-gated setters unauthenticated under `AccessControl::AuthControlled`: the `AuthSingleSigAcl` trigger list now contains every authority-gated setter root (`set_max_supply`, `set_description`, `set_logo_uri`, `set_external_link`, `set_mint_policy`, `set_burn_policy`, `set_send_policy`, `set_receive_policy`) in addition to `mint_and_send`. ([#2958](https://github.com/0xMiden/protocol/pull/2958)). - [BREAKING] Added missing transaction `ref_block_commitment` validation in `ProposedBatch::new` ([#2971](https://github.com/0xMiden/protocol/pull/2971)). ## 0.14.6 (2026-05-09) diff --git a/crates/miden-standards/src/account/faucets/fungible/mod.rs b/crates/miden-standards/src/account/faucets/fungible/mod.rs index 9b591bdbba..4f0bfe7979 100644 --- a/crates/miden-standards/src/account/faucets/fungible/mod.rs +++ b/crates/miden-standards/src/account/faucets/fungible/mod.rs @@ -388,12 +388,14 @@ impl FungibleFaucet { /// config + description + logo URI + external link + Pausable's `is_paused` flag). /// /// The `is_paused` slot is installed by FungibleFaucet itself (initial value: unpaused, zero - /// word) so that the transversal pause guards baked into `execute_mint_policy`, - /// `execute_burn_policy`, `check_policy` (allow_all / blocklist / allowlist) and the metadata + /// word) so that the transversal pause guards baked into mint / burn / transfer / metadata /// setters can read it without panicking. Pause / unpause administration is exposed by the /// [`crate::account::access::pausable::PausableManager`] component, which is bundled by /// [`create_user_fungible_faucet`] / [`create_network_fungible_faucet`] alongside this faucet - /// so the slot is always actionable. + /// so the slot is always actionable. The full [`Pausable`][crate::account::access::Pausable] + /// component (which exposes an `is_paused` view procedure) is intentionally NOT bundled — + /// it would install the same slot name and conflict with the faucet's own slot. Callers that + /// want the public view procedure should read the slot directly via account storage queries. pub fn into_storage_slots(self) -> Vec { let mut slots: Vec = Vec::new(); slots.push(self.token_config_slot_value()); @@ -568,7 +570,7 @@ impl TryFrom<&Account> for FungibleFaucet { /// gate for authority-protected setters ([`Authority::AuthControlled`] is installed directly). /// /// In addition to the explicit parameters, [`PausableManager`] is always bundled so the -/// `is_paused` slot installed by [`FungibleFaucet::into_storage_slots`] is actionable via +/// `is_paused` slot (installed by [`FungibleFaucet::into_storage_slots`]) is actionable via /// `pause` / `unpause` admin procedures (gated by the [`Authority::AuthControlled`] component /// installed by this factory). /// diff --git a/crates/miden-standards/src/account/wallets/mod.rs b/crates/miden-standards/src/account/wallets/mod.rs index 2e32c897b6..159b7f2345 100644 --- a/crates/miden-standards/src/account/wallets/mod.rs +++ b/crates/miden-standards/src/account/wallets/mod.rs @@ -10,7 +10,7 @@ use miden_protocol::account::{ use miden_protocol::errors::AccountError; use crate::account::account_component_code; -use crate::account::auth::AuthSingleSig; +use crate::account::auth::{AuthGuardedMultisig, AuthMultisig, AuthSingleSig}; use crate::procedure_root; // BASIC WALLET @@ -109,7 +109,8 @@ impl From for AccountComponent { /// - `move_asset_to_note`, which can be used to remove the specified asset from the account and add /// it to the output note with the specified index. /// -/// For wallets backed by other auth schemes (multisig variants), use [`AccountBuilder`] directly. +/// For multisig-authenticated basic wallets, use [`create_multisig_wallet`] or +/// [`create_guarded_wallet`]. For anything else, use [`AccountBuilder`] directly. pub fn create_basic_wallet( init_seed: [u8; 32], auth_component: AuthSingleSig, @@ -122,6 +123,34 @@ pub fn create_basic_wallet( .build() } +/// Creates a new account with the basic wallet interface authenticated by the provided +/// [`AuthMultisig`] component. Same procedures as [`create_basic_wallet`]. +pub fn create_multisig_wallet( + init_seed: [u8; 32], + auth_component: AuthMultisig, + account_type: AccountType, +) -> Result { + AccountBuilder::new(init_seed) + .account_type(account_type) + .with_auth_component(auth_component) + .with_component(BasicWallet) + .build() +} + +/// Creates a new account with the basic wallet interface authenticated by the provided +/// [`AuthGuardedMultisig`] component. Same procedures as [`create_basic_wallet`]. +pub fn create_guarded_wallet( + init_seed: [u8; 32], + auth_component: AuthGuardedMultisig, + account_type: AccountType, +) -> Result { + AccountBuilder::new(init_seed) + .account_type(account_type) + .with_auth_component(auth_component) + .with_component(BasicWallet) + .build() +} + // TESTS // ================================================================================================ @@ -131,7 +160,17 @@ mod tests { use miden_protocol::utils::serde::{Deserializable, Serializable}; use miden_protocol::{ONE, Word}; - use super::{Account, AccountType, AuthSingleSig, create_basic_wallet}; + use super::{ + Account, + AccountType, + AuthGuardedMultisig, + AuthMultisig, + AuthSingleSig, + create_basic_wallet, + create_guarded_wallet, + create_multisig_wallet, + }; + use crate::account::auth::{AuthGuardedMultisigConfig, AuthMultisigConfig, GuardianConfig}; use crate::account::wallets::BasicWallet; #[test] @@ -171,4 +210,37 @@ mod tests { let _receive_asset_root = BasicWallet::receive_asset_root(); let _move_asset_to_note_root = BasicWallet::move_asset_to_note_root(); } + + #[test] + fn test_create_multisig_wallet() { + let pub_key_1 = PublicKeyCommitment::from(Word::from([1u32, 0, 0, 0])); + let pub_key_2 = PublicKeyCommitment::from(Word::from([2u32, 0, 0, 0])); + let approvers = vec![ + (pub_key_1, auth::AuthScheme::Falcon512Poseidon2), + (pub_key_2, auth::AuthScheme::Falcon512Poseidon2), + ]; + let config = AuthMultisigConfig::new(approvers, 2).unwrap(); + let auth = AuthMultisig::new(config).unwrap(); + + let wallet = create_multisig_wallet([2; 32], auth, AccountType::Private); + wallet.unwrap_or_else(|err| panic!("{}", err)); + } + + #[test] + fn test_create_guarded_wallet() { + let pub_key_1 = PublicKeyCommitment::from(Word::from([1u32, 0, 0, 0])); + let pub_key_2 = PublicKeyCommitment::from(Word::from([2u32, 0, 0, 0])); + let guardian_key = PublicKeyCommitment::from(Word::from([3u32, 0, 0, 0])); + let approvers = vec![ + (pub_key_1, auth::AuthScheme::Falcon512Poseidon2), + (pub_key_2, auth::AuthScheme::Falcon512Poseidon2), + ]; + let guardian_config = + GuardianConfig::new(guardian_key, auth::AuthScheme::Falcon512Poseidon2); + let config = AuthGuardedMultisigConfig::new(approvers, 2, guardian_config).unwrap(); + let auth = AuthGuardedMultisig::new(config).unwrap(); + + let wallet = create_guarded_wallet([3; 32], auth, AccountType::Private); + wallet.unwrap_or_else(|err| panic!("{}", err)); + } } diff --git a/crates/miden-standards/src/testing/faucet.rs b/crates/miden-standards/src/testing/faucet.rs index 64014b148d..ead462d191 100644 --- a/crates/miden-standards/src/testing/faucet.rs +++ b/crates/miden-standards/src/testing/faucet.rs @@ -1,5 +1,7 @@ use alloc::vec; +use alloc::vec::Vec; +use miden_protocol::account::AccountProcedureRoot; use miden_protocol::account::auth::{AuthScheme, PublicKeyCommitment}; use miden_protocol::errors::AccountError; @@ -8,18 +10,15 @@ use crate::account::auth::{AuthSingleSigAcl, AuthSingleSigAclConfig}; use crate::account::faucets::FungibleFaucet; use crate::account::policies::TokenPolicyManager; -/// Convenience constructor for the typical user-account fungible faucet auth component: an -/// [`AuthSingleSigAcl`] with the trigger procedure list covering every authority-gated setter -/// (`mint_and_send`, metadata setters, policy setters, `pause` / `unpause`) and -/// `allow_unauthorized_input_notes=true`. +/// Returns every authority-gated setter procedure root exported by a fungible faucet account +/// (`mint_and_send`, the metadata setters, the policy setters, and `pause` / `unpause`). /// -/// Production callers that need a different ACL shape should construct [`AuthSingleSigAcl`] -/// directly. -pub fn user_faucet_single_sig_acl( - pub_key: PublicKeyCommitment, - scheme: AuthScheme, -) -> Result { - let trigger_procedures = vec![ +/// Under `Authority::AuthControlled` the auth component must authenticate calls to every +/// procedure in this list, otherwise the setters become permissionless. Use this when +/// constructing a custom [`AuthSingleSigAcl`] trigger procedure list; for the canonical +/// configuration, prefer [`user_faucet_single_sig_acl`]. +pub fn all_authority_gated_setter_roots() -> Vec { + vec![ FungibleFaucet::mint_and_send_root(), FungibleFaucet::set_max_supply_root(), FungibleFaucet::set_description_root(), @@ -31,12 +30,24 @@ pub fn user_faucet_single_sig_acl( TokenPolicyManager::set_receive_policy_root(), PausableManager::pause_root(), PausableManager::unpause_root(), - ]; + ] +} + +/// Convenience constructor for the typical user-account fungible faucet auth component: an +/// [`AuthSingleSigAcl`] with the trigger procedure list set to +/// [`all_authority_gated_setter_roots`] and `allow_unauthorized_input_notes=true`. +/// +/// Production callers that need a different ACL shape should construct [`AuthSingleSigAcl`] +/// directly, optionally seeding the trigger list with [`all_authority_gated_setter_roots`]. +pub fn user_faucet_single_sig_acl( + pub_key: PublicKeyCommitment, + scheme: AuthScheme, +) -> Result { AuthSingleSigAcl::new( pub_key, scheme, AuthSingleSigAclConfig::new() - .with_auth_trigger_procedures(trigger_procedures) + .with_auth_trigger_procedures(all_authority_gated_setter_roots()) .with_allow_unauthorized_input_notes(true), ) } diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index 2b72b85148..297ad9a81d 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -46,7 +46,7 @@ use miden_protocol::testing::account_id::ACCOUNT_ID_FEE_FAUCET; use miden_protocol::testing::random_secret_key::random_secret_key; use miden_protocol::transaction::{OrderedTransactionHeaders, RawOutputNote, TransactionKernel}; use miden_protocol::{MAX_OUTPUT_NOTES_PER_BATCH, Word}; -use miden_standards::account::access::{AccessControl, Authority}; +use miden_standards::account::access::{AccessControl, Authority, PausableManager}; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; use miden_standards::account::policies::{ BurnPolicy, @@ -316,6 +316,7 @@ impl MockChainBuilder { } /// Internal helper: adds an existing network-style fungible faucet (Ownable2Step / Rbac). + /// Bundles [`PausableManager`] to match the `create_network_fungible_faucet` factory. fn add_existing_network_fungible_faucet( &mut self, auth_method: Auth, @@ -328,7 +329,8 @@ impl MockChainBuilder { .account_type(account_type) .with_component(faucet) .with_components(access_control) - .with_components(token_policy_manager); + .with_components(token_policy_manager) + .with_component(PausableManager); self.add_account_from_builder(auth_method, account_builder, AccountState::Exists) } @@ -372,7 +374,8 @@ impl MockChainBuilder { .account_type(AccountType::Public) .with_component(faucet) .with_component(Authority::AuthControlled) - .with_components(token_policy_manager); + .with_components(token_policy_manager) + .with_component(PausableManager); self.add_account_from_builder(auth_method, account_builder, AccountState::Exists) } @@ -496,7 +499,8 @@ impl MockChainBuilder { .account_type(AccountType::Public) .with_component(faucet) .with_component(Authority::AuthControlled) - .with_components(token_policy_manager); + .with_components(token_policy_manager) + .with_component(PausableManager); self.add_account_from_builder(auth_method, account_builder, AccountState::New) } From 8c899401b5d67df18e39ad554422df83cbbb60fb Mon Sep 17 00:00:00 2001 From: onurinanc Date: Tue, 9 Jun 2026 15:23:35 +0200 Subject: [PATCH 11/12] add Pausable externally --- CHANGELOG.md | 3 +- .../src/account/faucets/fungible/mod.rs | 48 +++++++++---------- .../src/account/faucets/fungible/tests.rs | 16 ++----- .../src/kernel_tests/tx/test_account.rs | 2 + .../src/kernel_tests/tx/test_callbacks.rs | 3 +- .../src/mock_chain/chain_builder.rs | 5 +- .../src/standards/token_metadata.rs | 8 +++- .../miden-testing/tests/scripts/allowlist.rs | 3 +- .../miden-testing/tests/scripts/blocklist.rs | 3 +- crates/miden-testing/tests/scripts/faucet.rs | 11 +++-- .../miden-testing/tests/scripts/pausable.rs | 6 ++- 11 files changed, 61 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28735ad92c..5953317a5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,10 +17,11 @@ - Added `AccountComponent::has_procedure(root)` helper ([#2974](https://github.com/0xMiden/protocol/pull/2974)). - Optimized protocol MASM stack-cleaning sequences, saving 1 cycle per occurrence across 9 single-element-extraction procedures ([#3041](https://github.com/0xMiden/protocol/pull/3041)). - [BREAKING] Removed `AuthMethod` enum, `AccountAuthComponent` / `AccountAuthScheme`, and the `AccessControl::AuthControlled` variant. Faucet and wallet factories now take concrete auth-component types so invalid configurations are rejected at compile time ([#2944](https://github.com/0xMiden/protocol/pull/2944)). -- [BREAKING] Split `create_fungible_faucet` into `create_user_fungible_faucet(auth_component: AuthSingleSigAcl, ...)` (installs `Authority::AuthControlled` directly) and `create_network_fungible_faucet(access_control, auth_component: AuthNetworkAccount, ...)`. Other auth schemes are no longer supported through these helpers — fall back to `AccountBuilder` directly. A `user_faucet_single_sig_acl` testing helper is provided behind the `testing` feature ([#2944](https://github.com/0xMiden/protocol/pull/2944)). +- [BREAKING] Split `create_fungible_faucet` into `create_user_fungible_faucet(auth_component: AuthSingleSigAcl, ...)` (installs `Authority::AuthControlled` directly) and the opinionated `create_network_fungible_faucet(access_control, ...)` (always `AccountType::Public`, builds the `AuthNetworkAccount` allowlist internally from `MintNote` + `BurnNote` script roots with an empty tx-script allowlist). Other auth schemes / shapes are no longer supported through these helpers — fall back to `AccountBuilder` directly. A `user_faucet_single_sig_acl` testing helper is provided behind the `testing` feature ([#2944](https://github.com/0xMiden/protocol/pull/2944)). - Added `create_multisig_wallet` and `create_guarded_wallet` helpers for `BasicWallet` accounts authenticated by `AuthMultisig` and `AuthGuardedMultisig` respectively ([#2944](https://github.com/0xMiden/protocol/pull/2944)). - [BREAKING] `create_basic_wallet` now takes `AuthSingleSig` directly and returns `AccountError` instead of the removed `BasicWalletError` ([#2944](https://github.com/0xMiden/protocol/pull/2944)). - [BREAKING] Removed `AccountInterface::auth()` and `AccountComponentInterface::auth_scheme()`. Auth components are now discovered via `AccountInterface::auth_components()`, which iterates `AccountComponentInterface` variants flagged by `is_auth_component()` ([#2944](https://github.com/0xMiden/protocol/pull/2944)). +- [BREAKING] `FungibleFaucet` no longer installs the `is_paused` storage slot itself. Faucet factories (`create_user_fungible_faucet` / `create_network_fungible_faucet`) now bundle the `Pausable` component (slot + `is_paused()` view procedure) alongside `PausableManager`. Callers using `AccountBuilder` directly must also install `Pausable` or the faucet's mint / burn / transfer / metadata-setter procedures will panic at runtime ([#2944](https://github.com/0xMiden/protocol/pull/2944)). - [BREAKING] Refactored `TokenPolicyManager` by adding `invoke_send_policy` / `invoke_receive_policy` wrappers (stored in the protocol reserved asset callback slots) that read the active policy root from the new `active_send_policy_proc_root` / `active_receive_policy_proc_root` storage slots ([#3047](https://github.com/0xMiden/protocol/pull/3047)). - Added a definition of the Miden operator on the architecture overview page and linked it from the note lifecycle ([#3017](https://github.com/0xMiden/protocol/pull/3017)). - Clarified Miden's operational roles on the architecture overview page and linked them from the note lifecycle ([#3017](https://github.com/0xMiden/protocol/pull/3017)). diff --git a/crates/miden-standards/src/account/faucets/fungible/mod.rs b/crates/miden-standards/src/account/faucets/fungible/mod.rs index 4f0bfe7979..08da899a46 100644 --- a/crates/miden-standards/src/account/faucets/fungible/mod.rs +++ b/crates/miden-standards/src/account/faucets/fungible/mod.rs @@ -32,11 +32,12 @@ use super::{ TokenMetadataError, TokenName, }; -use crate::account::access::{AccessControl, Authority, PausableManager}; +use crate::account::access::{AccessControl, Authority, Pausable, PausableManager}; use crate::account::account_component_code; use crate::account::auth::{AuthNetworkAccount, AuthSingleSigAcl}; use crate::account::interface::{AccountComponentInterface, AccountInterface, AccountInterfaceExt}; use crate::account::policies::TokenPolicyManager; +use crate::note::{BurnNote, MintNote}; use crate::procedure_root; #[cfg(test)] @@ -385,22 +386,11 @@ impl FungibleFaucet { } /// Returns the storage slots produced by this faucet (token config word + name + mutability - /// config + description + logo URI + external link + Pausable's `is_paused` flag). - /// - /// The `is_paused` slot is installed by FungibleFaucet itself (initial value: unpaused, zero - /// word) so that the transversal pause guards baked into mint / burn / transfer / metadata - /// setters can read it without panicking. Pause / unpause administration is exposed by the - /// [`crate::account::access::pausable::PausableManager`] component, which is bundled by - /// [`create_user_fungible_faucet`] / [`create_network_fungible_faucet`] alongside this faucet - /// so the slot is always actionable. The full [`Pausable`][crate::account::access::Pausable] - /// component (which exposes an `is_paused` view procedure) is intentionally NOT bundled — - /// it would install the same slot name and conflict with the faucet's own slot. Callers that - /// want the public view procedure should read the slot directly via account storage queries. + /// config + description + logo URI + external link). pub fn into_storage_slots(self) -> Vec { let mut slots: Vec = Vec::new(); slots.push(self.token_config_slot_value()); slots.extend(self.metadata.into_storage_slots()); - slots.push(crate::account::access::pausable::PausableStorage::default().into_slot()); slots } @@ -569,11 +559,6 @@ impl TryFrom<&Account> for FungibleFaucet { /// Creates a new **user-account** fungible faucet. The account's auth component is the sole /// gate for authority-protected setters ([`Authority::AuthControlled`] is installed directly). /// -/// In addition to the explicit parameters, [`PausableManager`] is always bundled so the -/// `is_paused` slot (installed by [`FungibleFaucet::into_storage_slots`]) is actionable via -/// `pause` / `unpause` admin procedures (gated by the [`Authority::AuthControlled`] component -/// installed by this factory). -/// /// Caller passes a fully-configured [`AuthSingleSigAcl`] — its trigger procedure list must /// cover every authority-gated setter on the faucet (`mint_and_send`, the metadata setters, /// the policy setters, and `pause` / `unpause`), otherwise those procedures become @@ -591,29 +576,42 @@ pub fn create_user_fungible_faucet( .with_component(faucet) .with_component(Authority::AuthControlled) .with_components(token_policy_manager) + .with_component(Pausable::unpaused()) .with_component(PausableManager) .build() .map_err(FungibleFaucetError::AccountError) } -/// Creates a new **network-style** fungible faucet. Setter gating is enforced in-procedure by -/// the owner / role check installed via `access_control` ([`AccessControl::Ownable2Step`] or -/// [`AccessControl::Rbac`]). The auth component only governs the faucet's own transaction -/// authentication. +/// Creates a new **network-style** fungible faucet. The account is always +/// [`AccountType::Public`] (network accounts cannot be private). Setter gating is enforced +/// in-procedure by the owner / role check installed via `access_control` +/// ([`AccessControl::Ownable2Step`] or [`AccessControl::Rbac`]). +/// +/// The factory builds the [`AuthNetworkAccount`] auth component internally with a note +/// allowlist covering the faucet's own [`MintNote`] and [`BurnNote`] scripts and an empty +/// tx-script allowlist (network faucets are consumed via notes, not tx scripts). Callers +/// that need a custom allowlist (additional note scripts or tx scripts) should use +/// [`AccountBuilder`] directly. +/// +/// In addition to the explicit parameters, [`Pausable`] (slot + `is_paused` view) and +/// [`PausableManager`] (admin `pause` / `unpause` gated by `access_control`) are bundled. pub fn create_network_fungible_faucet( init_seed: [u8; 32], faucet: FungibleFaucet, access_control: AccessControl, - auth_component: AuthNetworkAccount, token_policy_manager: TokenPolicyManager, - account_type: AccountType, ) -> Result { + let note_allowlist = [MintNote::script_root(), BurnNote::script_root()].into_iter().collect(); + let auth_component = AuthNetworkAccount::with_allowed_notes(note_allowlist) + .expect("MintNote + BurnNote allowlist is non-empty"); + AccountBuilder::new(init_seed) - .account_type(account_type) + .account_type(AccountType::Public) .with_auth_component(auth_component) .with_component(faucet) .with_components(access_control) .with_components(token_policy_manager) + .with_component(Pausable::unpaused()) .with_component(PausableManager) .build() .map_err(FungibleFaucetError::AccountError) diff --git a/crates/miden-standards/src/account/faucets/fungible/tests.rs b/crates/miden-standards/src/account/faucets/fungible/tests.rs index ef35fb7fe0..41f0882485 100644 --- a/crates/miden-standards/src/account/faucets/fungible/tests.rs +++ b/crates/miden-standards/src/account/faucets/fungible/tests.rs @@ -4,12 +4,11 @@ use assert_matches::assert_matches; use miden_protocol::account::auth::{AuthScheme, PublicKeyCommitment}; use miden_protocol::account::{AccountBuilder, AccountType}; use miden_protocol::asset::{AssetAmount, TokenSymbol}; -use miden_protocol::note::NoteScriptRoot; use miden_protocol::{Felt, Word}; use super::{FungibleFaucet, create_network_fungible_faucet, create_user_fungible_faucet}; use crate::account::access::{AccessControl, PausableManager}; -use crate::account::auth::{AuthNetworkAccount, AuthSingleSig, AuthSingleSigAcl}; +use crate::account::auth::{AuthSingleSig, AuthSingleSigAcl}; use crate::account::faucets::{Description, FungibleFaucetError, TokenMetadata, TokenName}; use crate::account::policies::{BurnPolicy, MintPolicy, TokenPolicyManager, TransferPolicy}; use crate::account::wallets::BasicWallet; @@ -131,9 +130,9 @@ fn user_fungible_faucet_with_single_sig_acl() { let _faucet_component = FungibleFaucet::try_from(faucet_account.clone()).unwrap(); } -/// `create_network_fungible_faucet` with `Ownable2Step + AuthNetworkAccount` builds a valid -/// account. The auth component governs the faucet's own tx authentication; the setter gate is -/// enforced in-procedure by `assert_sender_is_owner`. +/// `create_network_fungible_faucet` with `Ownable2Step` builds a valid account. The factory +/// constructs `AuthNetworkAccount` internally; the setter gate is enforced in-procedure by +/// `assert_sender_is_owner`. #[test] fn network_fungible_faucet_with_ownable2step() { use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE; @@ -143,18 +142,13 @@ fn network_fungible_faucet_with_ownable2step() { ) .unwrap(); - let allowlist: BTreeSet = - [NoteScriptRoot::from_array([0; 4])].into_iter().collect(); - let _account = create_network_fungible_faucet( [7u8; 32], sample_faucet(), AccessControl::Ownable2Step { owner }, - AuthNetworkAccount::with_allowed_notes(allowlist).unwrap(), allow_all_policy_manager(), - AccountType::Public, ) - .expect("Ownable2Step+AuthNetworkAccount should be accepted"); + .expect("Ownable2Step network faucet should be accepted"); } #[test] diff --git a/crates/miden-testing/src/kernel_tests/tx/test_account.rs b/crates/miden-testing/src/kernel_tests/tx/test_account.rs index 6b5029efa5..6610abc3e7 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_account.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_account.rs @@ -59,6 +59,7 @@ use miden_protocol::testing::account_id::{ use miden_protocol::testing::storage::{MOCK_MAP_SLOT, MOCK_VALUE_SLOT0, MOCK_VALUE_SLOT1}; use miden_protocol::transaction::{RawOutputNote, TransactionKernel}; use miden_protocol::utils::sync::LazyLock; +use miden_standards::account::access::Pausable; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; use miden_standards::code_builder::CodeBuilder; use miden_standards::testing::account_component::MockAccountComponent; @@ -1647,6 +1648,7 @@ async fn test_faucet_has_callbacks( .account_type(AccountType::Public) .with_component(faucet) .with_component(MockAccountComponent::with_slots(callback_slots)) + .with_component(Pausable::unpaused()) .with_auth_component(Auth::IncrNonce) .build_existing()?; 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 9f469d94da..4fc5f676da 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_callbacks.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_callbacks.rs @@ -33,7 +33,7 @@ use miden_protocol::errors::MasmError; use miden_protocol::note::{NoteTag, NoteType}; use miden_protocol::utils::sync::LazyLock; use miden_protocol::{Felt, Word}; -use miden_standards::account::access::Authority; +use miden_standards::account::access::{Authority, Pausable}; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; use miden_standards::account::policies::{BurnPolicy, MintPolicy, TokenPolicyManager}; use miden_standards::code_builder::CodeBuilder; @@ -773,6 +773,7 @@ fn add_faucet_with_callbacks( .active_burn_policy(BurnPolicy::allow_all()) .build(), ) + .with_component(Pausable::unpaused()) .with_component(callback_component); builder.add_account_from_builder( diff --git a/crates/miden-testing/src/mock_chain/chain_builder.rs b/crates/miden-testing/src/mock_chain/chain_builder.rs index f70fee7e19..1296dce637 100644 --- a/crates/miden-testing/src/mock_chain/chain_builder.rs +++ b/crates/miden-testing/src/mock_chain/chain_builder.rs @@ -46,7 +46,7 @@ use miden_protocol::testing::account_id::ACCOUNT_ID_FEE_FAUCET; use miden_protocol::testing::random_secret_key::random_secret_key; use miden_protocol::transaction::{OrderedTransactionHeaders, RawOutputNote, TransactionKernel}; use miden_protocol::{MAX_OUTPUT_NOTES_PER_BATCH, Word}; -use miden_standards::account::access::{AccessControl, Authority, PausableManager}; +use miden_standards::account::access::{AccessControl, Authority, Pausable, PausableManager}; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; use miden_standards::account::policies::{ BurnPolicy, @@ -330,6 +330,7 @@ impl MockChainBuilder { .with_component(faucet) .with_components(access_control) .with_components(token_policy_manager) + .with_component(Pausable::unpaused()) .with_component(PausableManager); self.add_account_from_builder(auth_method, account_builder, AccountState::Exists) @@ -375,6 +376,7 @@ impl MockChainBuilder { .with_component(faucet) .with_component(Authority::AuthControlled) .with_components(token_policy_manager) + .with_component(Pausable::unpaused()) .with_component(PausableManager); self.add_account_from_builder(auth_method, account_builder, AccountState::Exists) @@ -506,6 +508,7 @@ impl MockChainBuilder { .with_component(faucet) .with_component(Authority::AuthControlled) .with_components(token_policy_manager) + .with_component(Pausable::unpaused()) .with_component(PausableManager); self.add_account_from_builder(auth_method, account_builder, AccountState::New) diff --git a/crates/miden-testing/src/standards/token_metadata.rs b/crates/miden-testing/src/standards/token_metadata.rs index 04427c31a6..bada9ef71c 100644 --- a/crates/miden-testing/src/standards/token_metadata.rs +++ b/crates/miden-testing/src/standards/token_metadata.rs @@ -21,6 +21,7 @@ use miden_protocol::asset::{AssetAmount, TokenSymbol}; use miden_protocol::errors::MasmError; use miden_protocol::note::{NoteTag, NoteType}; use miden_protocol::{Felt, Word}; +use miden_standards::account::access::Pausable; use miden_standards::account::auth::NoAuth; use miden_standards::account::faucets::{ Description, @@ -214,6 +215,7 @@ async fn get_name_from_masm() -> anyhow::Result<()> { let account = AccountBuilder::new([1u8; 32]) .with_auth_component(NoAuth) .with_component(faucet) + .with_component(Pausable::unpaused()) .build()?; execute_tx_script( @@ -249,6 +251,7 @@ async fn get_name_zeros_returns_empty() -> anyhow::Result<()> { let account = AccountBuilder::new([1u8; 32]) .with_auth_component(NoAuth) .with_component(faucet) + .with_component(Pausable::unpaused()) .build()?; execute_tx_script( @@ -406,6 +409,7 @@ async fn get_mutability_config() -> anyhow::Result<()> { let account = AccountBuilder::new([1u8; 32]) .with_auth_component(NoAuth) .with_component(faucet) + .with_component(Pausable::unpaused()) .build()?; execute_tx_script( @@ -448,6 +452,7 @@ async fn is_field_mutable_checks( let account = AccountBuilder::new([1u8; 32]) .with_auth_component(NoAuth) .with_component(faucet) + .with_component(Pausable::unpaused()) .build()?; execute_tx_script( @@ -525,7 +530,8 @@ fn verify_faucet_with_max_name_and_description( let mut builder = AccountBuilder::new(seed) .account_type(account_type) .with_auth_component(NoAuth) - .with_component(faucet); + .with_component(faucet) + .with_component(Pausable::unpaused()); for comp in extra_components { builder = builder.with_component(comp); diff --git a/crates/miden-testing/tests/scripts/allowlist.rs b/crates/miden-testing/tests/scripts/allowlist.rs index 8618ca817a..9de6a82fc8 100644 --- a/crates/miden-testing/tests/scripts/allowlist.rs +++ b/crates/miden-testing/tests/scripts/allowlist.rs @@ -16,7 +16,7 @@ use miden_protocol::asset::{Asset, AssetAmount, AssetCallbackFlag, FungibleAsset use miden_protocol::note::{Note, NoteTag, NoteType}; use miden_protocol::transaction::RawOutputNote; use miden_protocol::{Felt, Word}; -use miden_standards::account::access::{Authority, Ownable2Step}; +use miden_standards::account::access::{Authority, Ownable2Step, Pausable}; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; use miden_standards::account::policies::{ AllowlistOwnerControlled, @@ -86,6 +86,7 @@ fn add_faucet_with_owner_allowlist_transfer_initialized( .active_receive_policy(TransferPolicy::with_basic_allowlist(allow_list)) .build(), ) + .with_component(Pausable::unpaused()) .with_component(AllowlistOwnerControlled); builder.add_account_from_builder( diff --git a/crates/miden-testing/tests/scripts/blocklist.rs b/crates/miden-testing/tests/scripts/blocklist.rs index 59de9cae13..69082561c6 100644 --- a/crates/miden-testing/tests/scripts/blocklist.rs +++ b/crates/miden-testing/tests/scripts/blocklist.rs @@ -16,7 +16,7 @@ use miden_protocol::asset::{Asset, AssetAmount, AssetCallbackFlag, FungibleAsset use miden_protocol::note::{Note, NoteTag, NoteType}; use miden_protocol::transaction::RawOutputNote; use miden_protocol::{Felt, Word}; -use miden_standards::account::access::{Authority, Ownable2Step}; +use miden_standards::account::access::{Authority, Ownable2Step, Pausable}; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; use miden_standards::account::policies::{ BlocklistOwnerControlled, @@ -84,6 +84,7 @@ fn add_faucet_with_owner_blocklist_transfer_initialized( .active_receive_policy(TransferPolicy::empty_basic_blocklist()) .build(), ) + .with_component(Pausable::unpaused()) .with_component(BlocklistOwnerControlled); builder.add_account_from_builder( diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 06d0313ab1..f9723dea66 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -26,7 +26,7 @@ use miden_protocol::note::{ use miden_protocol::testing::account_id::ACCOUNT_ID_PRIVATE_SENDER; use miden_protocol::transaction::{ExecutedTransaction, RawOutputNote}; use miden_protocol::{Felt, Word}; -use miden_standards::account::access::{Authority, Ownable2Step}; +use miden_standards::account::access::{Authority, Ownable2Step, Pausable}; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; use miden_standards::account::policies::{ BurnAllowAll, @@ -238,7 +238,8 @@ fn build_network_faucet_with_burn_switching( .with_component(faucet) .with_component(Ownable2Step::new(owner)) .with_component(Authority::OwnerControlled) - .with_components(token_policy_manager); + .with_components(token_policy_manager) + .with_component(Pausable::unpaused()); builder.add_account_from_builder(Auth::IncrNonce, account_builder, AccountState::Exists) } @@ -280,7 +281,8 @@ fn build_network_faucet_with_min_burn_amount( .with_component(faucet) .with_component(Ownable2Step::new(owner)) .with_component(Authority::OwnerControlled) - .with_components(token_policy_manager); + .with_components(token_policy_manager) + .with_component(Pausable::unpaused()); builder.add_account_from_builder(Auth::IncrNonce, account_builder, AccountState::Exists) } @@ -2104,7 +2106,8 @@ fn build_network_faucet_with_blocklist_transfer( .with_component(faucet) .with_component(Ownable2Step::new(owner)) .with_component(Authority::OwnerControlled) - .with_components(token_policy_manager); + .with_components(token_policy_manager) + .with_component(Pausable::unpaused()); builder.add_account_from_builder( Auth::NetworkAccount { diff --git a/crates/miden-testing/tests/scripts/pausable.rs b/crates/miden-testing/tests/scripts/pausable.rs index 0f8b10c3c4..3df55e04fd 100644 --- a/crates/miden-testing/tests/scripts/pausable.rs +++ b/crates/miden-testing/tests/scripts/pausable.rs @@ -16,7 +16,7 @@ use miden_protocol::errors::MasmError; use miden_protocol::note::{Note, NoteTag, NoteType}; use miden_protocol::transaction::RawOutputNote; use miden_protocol::utils::sync::LazyLock; -use miden_standards::account::access::pausable::{PausableManager, PausableStorage}; +use miden_standards::account::access::pausable::{Pausable, PausableManager, PausableStorage}; use miden_standards::account::access::{AccessControl, Authority}; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; use miden_standards::account::policies::{ @@ -66,6 +66,7 @@ fn add_faucet_with_pause( .account_type(AccountType::Public) .with_component(faucet) .with_components(AccessControl::Ownable2Step { owner }) + .with_component(Pausable::unpaused()) .with_component(PausableManager); builder.add_account_from_builder(Auth::IncrNonce, account_builder, AccountState::Exists) @@ -293,6 +294,7 @@ fn add_faucet_with_pause_and_policies( .account_type(AccountType::Public) .with_component(faucet) .with_components(AccessControl::Ownable2Step { owner }) + .with_component(Pausable::unpaused()) .with_component(PausableManager) .with_components( TokenPolicyManager::builder() @@ -523,6 +525,7 @@ fn add_faucet_mutable_max_supply_with_pause( .account_type(AccountType::Public) .with_component(faucet) .with_components(AccessControl::Ownable2Step { owner }) + .with_component(Pausable::unpaused()) .with_component(PausableManager); builder.add_account_from_builder(Auth::IncrNonce, account_builder, AccountState::Exists) @@ -594,6 +597,7 @@ fn add_faucet_with_pause_auth_controlled( .account_type(AccountType::Public) .with_component(faucet) .with_component(Authority::AuthControlled) + .with_component(Pausable::unpaused()) .with_component(PausableManager); builder.add_account_from_builder(Auth::IncrNonce, account_builder, AccountState::Exists) From 0b9f759c7fe0b6facd4d33de3c47812a0b633cc4 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Thu, 11 Jun 2026 11:43:23 +0200 Subject: [PATCH 12/12] remove unnecessary file --- crates/miden-standards/src/auth_method.rs | 58 ----------------------- 1 file changed, 58 deletions(-) delete mode 100644 crates/miden-standards/src/auth_method.rs diff --git a/crates/miden-standards/src/auth_method.rs b/crates/miden-standards/src/auth_method.rs deleted file mode 100644 index 86c0306e2a..0000000000 --- a/crates/miden-standards/src/auth_method.rs +++ /dev/null @@ -1,58 +0,0 @@ -use alloc::collections::BTreeSet; -use alloc::vec::Vec; - -use miden_protocol::account::auth::{AuthScheme, PublicKeyCommitment}; -use miden_protocol::note::NoteScriptRoot; -use miden_protocol::transaction::TransactionScriptRoot; - -/// Defines standard authentication methods supported by account auth components. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum AuthMethod { - /// A minimal authentication method that provides no cryptographic authentication. - /// - /// It only increments the nonce if the account state has actually changed during transaction - /// execution, avoiding unnecessary nonce increments for transactions that don't modify the - /// account state. - NoAuth, - /// A single-key authentication method which relies on either ECDSA or Falcon512Poseidon2 - /// signatures. - SingleSig { - approver: (PublicKeyCommitment, AuthScheme), - }, - /// A multi-signature authentication method using either ECDSA or Falcon512Poseidon2 signatures. - /// - /// Requires a threshold number of signatures from the provided public keys. - Multisig { - threshold: u32, - approvers: Vec<(PublicKeyCommitment, AuthScheme)>, - }, - /// An authentication method intended for network-owned accounts. - /// - /// It restricts the account to consuming only notes whose script roots are in - /// `allowed_script_roots` (which must be non-empty), and to executing only transaction scripts - /// whose roots are in `allowed_tx_script_roots`. An empty `allowed_tx_script_roots` permits no - /// transaction scripts. - NetworkAccount { - allowed_script_roots: BTreeSet, - allowed_tx_script_roots: BTreeSet, - }, - /// A non-standard authentication method. - Unknown, -} - -impl AuthMethod { - /// Returns all public key commitments associated with this authentication method. - /// - /// For unknown methods, an empty vector is returned. - pub fn get_public_key_commitments(&self) -> Vec { - match self { - AuthMethod::NoAuth => Vec::new(), - AuthMethod::SingleSig { approver: (pub_key, _) } => vec![*pub_key], - AuthMethod::Multisig { approvers, .. } => { - approvers.iter().map(|(pub_key, _)| *pub_key).collect() - }, - AuthMethod::NetworkAccount { .. } => Vec::new(), - AuthMethod::Unknown => Vec::new(), - } - } -}