gmsol_store/states/
roles.rs1use anchor_lang::prelude::*;
2
3use gmsol_utils::bitmaps::Bitmap;
4
5use crate::CoreError;
6
7use super::InitSpace;
8
9pub use gmsol_utils::role::{RoleKey, MAX_ROLE_NAME_LEN};
10
11pub const MAX_ROLES: usize = 32;
13
14pub const MAX_MEMBERS: usize = 64;
16
17type RoleBitmap = Bitmap<MAX_ROLES>;
18type RoleBitmapValue = u32;
19
20#[zero_copy]
22#[derive(Default)]
23#[cfg_attr(feature = "debug", derive(Debug))]
24pub struct RoleMetadata {
25 name: [u8; MAX_ROLE_NAME_LEN],
26 enabled: u8,
27 index: u8,
28}
29
30impl InitSpace for RoleMetadata {
31 const INIT_SPACE: usize = 32 + 2;
32}
33
34#[cfg(test)]
35const_assert_eq!(
36 std::mem::size_of::<RoleMetadata>(),
37 RoleMetadata::INIT_SPACE
38);
39
40impl RoleMetadata {
41 pub const ROLE_ENABLED: u8 = u8::MAX;
43
44 fn name_to_bytes(name: &str) -> Result<[u8; MAX_ROLE_NAME_LEN]> {
45 crate::utils::fixed_str::fixed_str_to_bytes(name)
46 }
47
48 fn bytes_to_name(bytes: &[u8; 32]) -> Result<&str> {
49 crate::utils::fixed_str::bytes_to_fixed_str(bytes)
50 }
51
52 pub fn new(name: &str, index: u8) -> Result<Self> {
54 Ok(Self {
55 name: Self::name_to_bytes(name)?,
56 enabled: Self::ROLE_ENABLED,
57 index,
58 })
59 }
60
61 pub fn name(&self) -> Result<&str> {
63 Self::bytes_to_name(&self.name)
64 }
65
66 pub fn enable(&mut self) -> Result<()> {
71 require!(!self.is_enabled(), CoreError::PreconditionsAreNotMet);
72 self.set_enable();
73 Ok(())
74 }
75
76 pub fn disable(&mut self) -> Result<()> {
81 require!(self.is_enabled(), CoreError::PreconditionsAreNotMet);
82 self.set_disable();
83 Ok(())
84 }
85
86 fn set_enable(&mut self) {
88 self.enabled = Self::ROLE_ENABLED;
89 }
90
91 fn set_disable(&mut self) {
93 self.enabled = 0;
94 }
95
96 pub fn is_enabled(&self) -> bool {
98 self.enabled == Self::ROLE_ENABLED
99 }
100}
101
102gmsol_utils::fixed_map!(RoleMap, RoleMetadata, MAX_ROLES, 0);
103
104gmsol_utils::fixed_map!(
105 Members,
106 Pubkey,
107 crate::utils::pubkey::to_bytes,
108 u32,
109 MAX_MEMBERS,
110 0
111);
112
113#[zero_copy]
115#[cfg_attr(feature = "debug", derive(Debug))]
116pub struct RoleStore {
117 roles: RoleMap,
118 members: Members,
119}
120
121impl InitSpace for RoleStore {
122 const INIT_SPACE: usize = std::mem::size_of::<RoleStore>();
123}
124
125impl RoleStore {
126 pub fn enable_role(&mut self, role: &str) -> Result<()> {
128 match self.roles.get_mut(role) {
129 Some(metadata) => {
130 require_eq!(metadata.name()?, role, CoreError::InvalidArgument);
131 metadata.enable()?;
132 }
133 None => {
134 let index = self
135 .roles
136 .len()
137 .try_into()
138 .map_err(|_| error!(CoreError::ExceedMaxLengthLimit))?;
139 self.roles
140 .insert_with_options(role, RoleMetadata::new(role, index)?, true)?;
141 }
142 }
143 Ok(())
144 }
145
146 pub fn disable_role(&mut self, role: &str) -> Result<()> {
148 if let Some(metadata) = self.roles.get_mut(role) {
149 require_eq!(metadata.name()?, role, CoreError::InvalidArgument);
150 metadata.disable()?;
151 }
152 Ok(())
153 }
154
155 pub fn role_index(&self, role: &str) -> Result<Option<u8>> {
157 if let Some(metadata) = self.roles.get(role) {
158 require_eq!(metadata.name()?, role, CoreError::InvalidArgument);
159 Ok(Some(metadata.index))
160 } else {
161 Ok(None)
162 }
163 }
164
165 pub fn enabled_role_index(&self, role: &str) -> Result<Option<u8>> {
167 if let Some(metadata) = self.roles.get(role) {
168 require_eq!(metadata.name()?, role, CoreError::InvalidArgument);
169 require!(metadata.is_enabled(), CoreError::PreconditionsAreNotMet);
170 Ok(Some(metadata.index))
171 } else {
172 Ok(None)
173 }
174 }
175
176 pub fn has_role(&self, authority: &Pubkey, role: &str) -> Result<bool> {
178 let Some(value) = self.members.get(authority) else {
179 return err!(CoreError::PermissionDenied);
180 };
181 let Some(index) = self.enabled_role_index(role)? else {
182 return err!(CoreError::NotFound);
183 };
184 let bitmap = RoleBitmap::from_value(*value);
185 Ok(bitmap.get(index as usize))
186 }
187
188 pub fn grant(&mut self, authority: &Pubkey, role: &str) -> Result<()> {
194 let Some(index) = self.enabled_role_index(role)? else {
195 return err!(CoreError::NotFound);
196 };
197 let index = index as usize;
198 match self.members.get_mut(authority) {
199 Some(value) => {
200 let mut bitmap = RoleBitmap::from_value(*value);
201 require!(!bitmap.get(index), CoreError::PreconditionsAreNotMet);
202 bitmap.set(index, true);
203 *value = bitmap.into_value();
204 }
205 None => {
206 let mut bitmap = RoleBitmap::new();
207 bitmap.set(index, true);
208 self.members
209 .insert_with_options(authority, bitmap.into_value(), true)?;
210 }
211 }
212 Ok(())
213 }
214
215 pub fn revoke(&mut self, authority: &Pubkey, role: &str) -> Result<()> {
220 let Some(index) = self.role_index(role)? else {
223 return err!(CoreError::NotFound);
224 };
225 let Some(value) = self.members.get_mut(authority) else {
226 return err!(CoreError::PermissionDenied);
227 };
228 let mut bitmap = RoleBitmap::from_value(*value);
229 let index = index as usize;
230 require!(bitmap.get(index), CoreError::PreconditionsAreNotMet);
231 bitmap.set(index, false);
232 *value = bitmap.into_value();
233
234 if bitmap.is_empty() {
236 self.members.remove(authority);
237 }
238
239 Ok(())
240 }
241
242 pub fn num_roles(&self) -> usize {
244 self.roles.len()
245 }
246
247 pub fn num_members(&self) -> usize {
249 self.members.len()
250 }
251
252 pub fn role_value(&self, user: &Pubkey) -> Option<RoleBitmapValue> {
254 self.members.get(user).copied()
255 }
256
257 pub fn members(&self) -> impl Iterator<Item = Pubkey> + '_ {
259 self.members
260 .entries()
261 .map(|(key, _)| Pubkey::new_from_array(*key))
262 }
263
264 pub fn roles(&self) -> impl Iterator<Item = Result<&str>> + '_ {
266 self.roles.entries().map(|(_, value)| value.name())
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use bytemuck::Zeroable;
273
274 use super::*;
275
276 #[test]
277 fn grant_and_revoke_roles() {
278 let mut store = RoleStore::zeroed();
279 let authority = Pubkey::new_unique();
280
281 assert!(store.grant(&authority, RoleKey::GT_CONTROLLER).is_err());
282 assert!(store.has_role(&authority, RoleKey::GT_CONTROLLER).is_err());
283
284 store.enable_role(RoleKey::GT_CONTROLLER).unwrap();
285 store.enable_role(RoleKey::MARKET_KEEPER).unwrap();
286
287 store.grant(&authority, RoleKey::GT_CONTROLLER).unwrap();
288 assert_eq!(store.has_role(&authority, RoleKey::GT_CONTROLLER), Ok(true));
289 store.grant(&authority, RoleKey::MARKET_KEEPER).unwrap();
290 assert_eq!(store.has_role(&authority, RoleKey::MARKET_KEEPER), Ok(true));
291 assert_eq!(store.has_role(&authority, RoleKey::GT_CONTROLLER), Ok(true));
292
293 store.revoke(&authority, RoleKey::GT_CONTROLLER).unwrap();
294 assert_eq!(store.has_role(&authority, RoleKey::MARKET_KEEPER), Ok(true));
295 assert_eq!(
296 store.has_role(&authority, RoleKey::GT_CONTROLLER),
297 Ok(false)
298 );
299
300 store.revoke(&authority, RoleKey::MARKET_KEEPER).unwrap();
303 assert!(store.has_role(&authority, RoleKey::MARKET_KEEPER).is_err());
304 assert!(store.has_role(&authority, RoleKey::GT_CONTROLLER).is_err());
305
306 store.disable_role(RoleKey::MARKET_KEEPER).unwrap();
307 assert!(store.grant(&authority, RoleKey::MARKET_KEEPER).is_err());
308 assert!(store.has_role(&authority, RoleKey::MARKET_KEEPER).is_err());
309 store.enable_role(RoleKey::MARKET_KEEPER).unwrap();
310 store.grant(&authority, RoleKey::MARKET_KEEPER).unwrap();
311 assert_eq!(store.has_role(&authority, RoleKey::MARKET_KEEPER), Ok(true));
312 }
313
314 #[test]
315 fn enable_and_disable_role() {
316 let mut store = RoleStore::zeroed();
317 let authority = Pubkey::new_unique();
318
319 store.enable_role(RoleKey::GT_CONTROLLER).unwrap();
320 store.grant(&authority, RoleKey::GT_CONTROLLER).unwrap();
321 assert_eq!(store.has_role(&authority, RoleKey::GT_CONTROLLER), Ok(true));
322 store.disable_role(RoleKey::GT_CONTROLLER).unwrap();
323 assert!(store.has_role(&authority, RoleKey::GT_CONTROLLER).is_err());
324 store.enable_role(RoleKey::GT_CONTROLLER).unwrap();
325 assert_eq!(store.has_role(&authority, RoleKey::GT_CONTROLLER), Ok(true));
326 }
327}