miden_standards/account/access/
authority.rs1use miden_protocol::account::component::{
2 AccountComponentCode,
3 AccountComponentMetadata,
4 FeltSchema,
5 StorageSchema,
6 StorageSlotSchema,
7};
8use miden_protocol::account::{
9 AccountComponent,
10 AccountStorage,
11 RoleSymbol,
12 StorageSlot,
13 StorageSlotName,
14};
15use miden_protocol::errors::{AccountError, RoleSymbolError};
16use miden_protocol::utils::sync::LazyLock;
17use miden_protocol::{Felt, Word};
18use thiserror::Error;
19
20use crate::account::account_component_code;
21
22account_component_code!(AUTHORITY_CODE, "access/authority.masl");
26
27static AUTHORITY_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
28 StorageSlotName::new("miden::standards::access::authority")
29 .expect("storage slot name should be valid")
30});
31
32const AUTH_CONTROLLED: u8 = 0;
34const OWNER_CONTROLLED: u8 = 1;
36const RBAC_CONTROLLED: u8 = 2;
38
39#[repr(u8)]
58#[derive(Debug, Clone, PartialEq, Eq)]
59#[non_exhaustive]
60pub enum Authority {
61 AuthControlled = AUTH_CONTROLLED,
64 OwnerControlled = OWNER_CONTROLLED,
67 RbacControlled { role: RoleSymbol } = RBAC_CONTROLLED,
75}
76
77impl Authority {
78 pub const NAME: &'static str = "miden::standards::components::access::authority";
80
81 pub fn code() -> &'static AccountComponentCode {
83 &AUTHORITY_CODE
84 }
85
86 pub fn authority_slot() -> &'static StorageSlotName {
91 &AUTHORITY_SLOT_NAME
92 }
93
94 pub fn try_from_storage(storage: &AccountStorage) -> Result<Self, AuthorityError> {
96 let word = storage
97 .get_item(Self::authority_slot())
98 .map_err(AuthorityError::MissingStorageSlot)?;
99 Self::try_from(word)
100 }
101
102 pub fn component_metadata() -> AccountComponentMetadata {
104 let storage_schema = StorageSchema::new(vec![(
105 AUTHORITY_SLOT_NAME.clone(),
106 StorageSlotSchema::value(
107 "Authority configuration",
108 [
109 FeltSchema::u8("authority"),
110 FeltSchema::felt("role_symbol"),
111 FeltSchema::new_void(),
112 FeltSchema::new_void(),
113 ],
114 ),
115 )])
116 .expect("storage schema should be valid");
117
118 AccountComponentMetadata::new(Self::NAME)
119 .with_description(
120 "Account-wide authority shared by procedures that gate state-mutating \
121 operations behind auth-only, owner-based, or RBAC role-based checks",
122 )
123 .with_storage_schema(storage_schema)
124 }
125}
126
127impl From<Authority> for Word {
131 fn from(value: Authority) -> Self {
132 match value {
133 Authority::AuthControlled => {
134 Word::new([Felt::from(AUTH_CONTROLLED), Felt::ZERO, Felt::ZERO, Felt::ZERO])
135 },
136 Authority::OwnerControlled => {
137 Word::new([Felt::from(OWNER_CONTROLLED), Felt::ZERO, Felt::ZERO, Felt::ZERO])
138 },
139 Authority::RbacControlled { role } => {
140 Word::new([Felt::from(RBAC_CONTROLLED), role.into(), Felt::ZERO, Felt::ZERO])
141 },
142 }
143 }
144}
145
146impl TryFrom<Word> for Authority {
147 type Error = AuthorityError;
148
149 fn try_from(word: Word) -> Result<Self, Self::Error> {
150 let authority: u8 = word[0]
151 .as_canonical_u64()
152 .try_into()
153 .map_err(|_| AuthorityError::InvalidAuthority(word[0].as_canonical_u64()))?;
154 match authority {
155 AUTH_CONTROLLED => Ok(Self::AuthControlled),
156 OWNER_CONTROLLED => Ok(Self::OwnerControlled),
157 RBAC_CONTROLLED => {
158 let role =
159 RoleSymbol::try_from(word[1]).map_err(AuthorityError::InvalidRoleSymbol)?;
160 Ok(Self::RbacControlled { role })
161 },
162 other => Err(AuthorityError::InvalidAuthority(other.into())),
163 }
164 }
165}
166
167impl From<Authority> for AccountComponent {
168 fn from(value: Authority) -> Self {
169 let slot = StorageSlot::with_value(AUTHORITY_SLOT_NAME.clone(), Word::from(value));
170 AccountComponent::new(
171 Authority::code().clone(),
172 vec![slot],
173 Authority::component_metadata(),
174 )
175 .expect("authority component should satisfy the requirements of a valid account component")
176 }
177}
178
179#[derive(Debug, Error)]
184pub enum AuthorityError {
185 #[error("invalid authority value: {0}")]
186 InvalidAuthority(u64),
187 #[error("invalid role symbol in authority slot")]
188 InvalidRoleSymbol(#[source] RoleSymbolError),
189 #[error("failed to read authority slot from storage")]
190 MissingStorageSlot(#[source] AccountError),
191}