Skip to main content

miden_standards/account/access/
mod.rs

1use alloc::collections::BTreeMap;
2use alloc::vec;
3
4use miden_protocol::Felt;
5use miden_protocol::account::{AccountComponent, AccountId, AccountProcedureRoot, RoleSymbol};
6use miden_protocol::errors::AccountIdError;
7
8pub mod authority;
9pub mod ownable2step;
10pub mod pausable;
11pub mod rbac;
12pub mod warden;
13
14/// Access control configuration for network-style accounts whose authority-gated setters are
15/// gated by an owner / role check rather than by the account's auth component.
16///
17/// User-account faucets (where the auth component is itself the setter gate) install
18/// [`Authority::AuthControlled`] directly via factories like
19/// [`create_singlesig_user_fungible_faucet`][crate::account::faucets::create_singlesig_user_fungible_faucet];
20/// they do not need this enum.
21///
22/// - [`AccessControl::Ownable2Step`] → [`Ownable2Step`] + [`Authority::OwnerControlled`]. The
23///   setter gate enforces `sender == owner`.
24/// - [`AccessControl::Rbac`] → [`RoleBasedAccessControl`] + [`Authority::RbacControlled`]. The
25///   `roles` map assigns a role to individual gated procedures (keyed by procedure root);
26///   procedures without a mapping fall back to the `ADMIN` role check.
27///
28/// Pass to
29/// [`AccountBuilder::with_components`][miden_protocol::account::AccountBuilder::with_components]
30/// to install the access control components on the account:
31///
32/// ```no_run
33/// use std::collections::BTreeMap;
34///
35/// use miden_protocol::account::AccountBuilder;
36/// use miden_standards::account::access::AccessControl;
37/// # let admin: miden_protocol::account::AccountId = unimplemented!();
38/// # let init_seed = [0u8; 32];
39/// AccountBuilder::new(init_seed)
40///     .with_components(AccessControl::Rbac { admin, roles: BTreeMap::new() });
41/// ```
42///
43/// For accounts that don't use the [`AccessControl`] convenience but want to install the
44/// [`Authority`] component directly, the [`Authority`] enum can be passed via
45/// [`AccountBuilder::with_component`][miden_protocol::account::AccountBuilder::with_component].
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub enum AccessControl {
48    /// Two-step ownership transfer with the provided initial owner. The setter gate enforces
49    /// `sender == owner`.
50    Ownable2Step { owner: AccountId },
51    /// Role-based access control. The provided `admin` is seeded as the initial member of the
52    /// RBAC `ADMIN` role, which bootstraps role administration.
53    ///
54    /// Role administration itself is fully role-based. Each role is managed by its effective
55    /// admin role (its delegated admin, or `ADMIN` by default). See [`RoleBasedAccessControl`]
56    /// for the administration model.
57    ///
58    /// `roles` assigns a role to individual authority-gated procedures, keyed by procedure root
59    /// (e.g. `PausableManager::pause_root()` → `PAUSER`, `unpause_root()` → `UNPAUSER`, and
60    /// optionally `Authority::freeze_root()` → `FREEZER`). A gated procedure without an entry in
61    /// `roles` falls back to the `ADMIN` role. The emergency `freeze` / `unfreeze` switch resolves
62    /// its role the same way, defaulting to `ADMIN`. Role membership is managed through the
63    /// standard RBAC API on the [`RoleBasedAccessControl`] component.
64    Rbac {
65        admin: AccountId,
66        roles: BTreeMap<AccountProcedureRoot, RoleSymbol>,
67    },
68}
69
70impl IntoIterator for AccessControl {
71    type Item = AccountComponent;
72    type IntoIter = alloc::vec::IntoIter<AccountComponent>;
73
74    /// Yields the [`AccountComponent`]s implementing this access control configuration, in the
75    /// order they must be installed on the account. The matching [`Authority`] component is
76    /// always included.
77    fn into_iter(self) -> Self::IntoIter {
78        match self {
79            AccessControl::Ownable2Step { owner } => {
80                vec![Ownable2Step::new(owner).into(), Authority::OwnerControlled.into()].into_iter()
81            },
82            AccessControl::Rbac { admin, roles } => vec![
83                RoleBasedAccessControl::new(admin).into(),
84                Authority::RbacControlled { roles }.into(),
85            ]
86            .into_iter(),
87        }
88    }
89}
90
91pub use authority::{Authority, AuthorityError};
92pub use ownable2step::{Ownable2Step, Ownable2StepError};
93pub use pausable::{Pausable, PausableManager, PausableStorage};
94pub use rbac::RoleBasedAccessControl;
95pub use warden::{Warden, WardenError};
96
97// HELPERS
98// ================================================================================================
99
100/// Constructs an `Option<AccountId>` from a suffix/prefix felt pair.
101/// Returns `Ok(None)` when both felts are zero (e.g. no owner / no nomination / no warden).
102pub(crate) fn account_id_from_felt_pair(
103    suffix: Felt,
104    prefix: Felt,
105) -> Result<Option<AccountId>, AccountIdError> {
106    if suffix == Felt::ZERO && prefix == Felt::ZERO {
107        Ok(None)
108    } else {
109        AccountId::try_from_elements(suffix, prefix).map(Some)
110    }
111}