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