casper_types/
access_rights.rs

1use alloc::{
2    collections::{btree_map::Entry, BTreeMap},
3    vec::Vec,
4};
5use core::fmt::{self, Display, Formatter};
6
7#[cfg(any(feature = "testing", test))]
8use rand::{
9    distributions::{Distribution, Standard},
10    Rng,
11};
12use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
13
14use crate::{bytesrepr, HashAddr, URef, URefAddr};
15pub use private::AccessRights;
16
17/// The number of bytes in a serialized [`AccessRights`].
18pub const ACCESS_RIGHTS_SERIALIZED_LENGTH: usize = 1;
19
20// Module exists only to restrict the scope of the following `#allow`.
21#[allow(clippy::bad_bit_mask)]
22mod private {
23    use bitflags::bitflags;
24    #[cfg(feature = "datasize")]
25    use datasize::DataSize;
26
27    bitflags! {
28        /// A struct which behaves like a set of bitflags to define access rights associated with a
29        /// [`URef`](crate::URef).
30        #[allow(clippy::derived_hash_with_manual_eq)]
31        #[cfg_attr(feature = "datasize", derive(DataSize))]
32        pub struct AccessRights: u8 {
33            /// No permissions
34            const NONE = 0;
35            /// Permission to read the value under the associated `URef`.
36            const READ  = 0b001;
37            /// Permission to write a value under the associated `URef`.
38            const WRITE = 0b010;
39            /// Permission to add to the value under the associated `URef`.
40            const ADD   = 0b100;
41            /// Permission to read or add to the value under the associated `URef`.
42            const READ_ADD       = Self::READ.bits | Self::ADD.bits;
43            /// Permission to read or write the value under the associated `URef`.
44            const READ_WRITE     = Self::READ.bits | Self::WRITE.bits;
45            /// Permission to add to, or write the value under the associated `URef`.
46            const ADD_WRITE      = Self::ADD.bits  | Self::WRITE.bits;
47            /// Permission to read, add to, or write the value under the associated `URef`.
48            const READ_ADD_WRITE = Self::READ.bits | Self::ADD.bits | Self::WRITE.bits;
49        }
50    }
51}
52
53impl Default for AccessRights {
54    fn default() -> Self {
55        AccessRights::NONE
56    }
57}
58
59impl AccessRights {
60    /// Returns `true` if the `READ` flag is set.
61    pub fn is_readable(self) -> bool {
62        self & AccessRights::READ == AccessRights::READ
63    }
64
65    /// Returns `true` if the `WRITE` flag is set.
66    pub fn is_writeable(self) -> bool {
67        self & AccessRights::WRITE == AccessRights::WRITE
68    }
69
70    /// Returns `true` if the `ADD` flag is set.
71    pub fn is_addable(self) -> bool {
72        self & AccessRights::ADD == AccessRights::ADD
73    }
74
75    /// Returns `true` if no flags are set.
76    pub fn is_none(self) -> bool {
77        self == AccessRights::NONE
78    }
79}
80
81impl Display for AccessRights {
82    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
83        match *self {
84            AccessRights::NONE => write!(f, "NONE"),
85            AccessRights::READ => write!(f, "READ"),
86            AccessRights::WRITE => write!(f, "WRITE"),
87            AccessRights::ADD => write!(f, "ADD"),
88            AccessRights::READ_ADD => write!(f, "READ_ADD"),
89            AccessRights::READ_WRITE => write!(f, "READ_WRITE"),
90            AccessRights::ADD_WRITE => write!(f, "ADD_WRITE"),
91            AccessRights::READ_ADD_WRITE => write!(f, "READ_ADD_WRITE"),
92            _ => write!(f, "UNKNOWN"),
93        }
94    }
95}
96
97impl bytesrepr::ToBytes for AccessRights {
98    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
99        self.bits().to_bytes()
100    }
101
102    fn serialized_length(&self) -> usize {
103        ACCESS_RIGHTS_SERIALIZED_LENGTH
104    }
105
106    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
107        writer.push(self.bits());
108        Ok(())
109    }
110}
111
112impl bytesrepr::FromBytes for AccessRights {
113    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
114        let (id, rem) = u8::from_bytes(bytes)?;
115        match AccessRights::from_bits(id) {
116            Some(rights) => Ok((rights, rem)),
117            None => Err(bytesrepr::Error::Formatting),
118        }
119    }
120}
121
122impl Serialize for AccessRights {
123    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
124        self.bits().serialize(serializer)
125    }
126}
127
128impl<'de> Deserialize<'de> for AccessRights {
129    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
130        let bits = u8::deserialize(deserializer)?;
131        AccessRights::from_bits(bits).ok_or_else(|| SerdeError::custom("invalid bits"))
132    }
133}
134
135#[cfg(any(feature = "testing", test))]
136impl Distribution<AccessRights> for Standard {
137    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> AccessRights {
138        let mut result = AccessRights::NONE;
139        if rng.gen() {
140            result |= AccessRights::READ;
141        }
142        if rng.gen() {
143            result |= AccessRights::WRITE;
144        }
145        if rng.gen() {
146            result |= AccessRights::ADD;
147        }
148        result
149    }
150}
151
152/// Used to indicate if a granted [`URef`] was already held by the context.
153#[derive(Debug, PartialEq, Eq)]
154pub enum GrantedAccess {
155    /// No new set of access rights were granted.
156    PreExisting,
157    /// A new set of access rights were granted.
158    Granted {
159        /// The address of the URef.
160        uref_addr: URefAddr,
161        /// The set of the newly granted access rights.
162        newly_granted_access_rights: AccessRights,
163    },
164}
165
166/// Access rights for a given runtime context.
167#[derive(Debug, PartialEq, Eq)]
168pub struct ContextAccessRights {
169    hash_addr: HashAddr,
170    access_rights: BTreeMap<URefAddr, AccessRights>,
171}
172
173impl ContextAccessRights {
174    /// Creates a new instance of access rights from an iterator of URefs merging any duplicates,
175    /// taking the union of their rights.
176    pub fn new<T: IntoIterator<Item = URef>>(hash_addr: HashAddr, uref_iter: T) -> Self {
177        let mut context_access_rights = ContextAccessRights {
178            hash_addr,
179            access_rights: BTreeMap::new(),
180        };
181        context_access_rights.do_extend(uref_iter);
182        context_access_rights
183    }
184
185    /// Extend context access rights with access rights.
186    pub fn extend_access_rights(&mut self, access_rights: BTreeMap<URefAddr, AccessRights>) {
187        for (uref_addr, access_rights) in access_rights {
188            match self.access_rights.entry(uref_addr) {
189                Entry::Occupied(rights) => {
190                    *rights.into_mut() = rights.get().union(access_rights);
191                }
192                Entry::Vacant(rights) => {
193                    rights.insert(access_rights);
194                }
195            }
196        }
197    }
198
199    /// Returns the current context key.
200    pub fn context_key(&self) -> HashAddr {
201        self.hash_addr
202    }
203
204    /// Extends the current access rights from a given set of URefs.
205    pub fn extend(&mut self, urefs: &[URef]) {
206        self.do_extend(urefs.iter().copied())
207    }
208
209    /// Extends the current access rights from a given set of URefs.
210    fn do_extend<T: IntoIterator<Item = URef>>(&mut self, uref_iter: T) {
211        for uref in uref_iter {
212            match self.access_rights.entry(uref.addr()) {
213                Entry::Occupied(rights) => {
214                    *rights.into_mut() = rights.get().union(uref.access_rights());
215                }
216                Entry::Vacant(rights) => {
217                    rights.insert(uref.access_rights());
218                }
219            }
220        }
221    }
222
223    /// Checks whether given uref has enough access rights.
224    pub fn has_access_rights_to_uref(&self, uref: &URef) -> bool {
225        if let Some(known_rights) = self.access_rights.get(&uref.addr()) {
226            let rights_to_check = uref.access_rights();
227            known_rights.contains(rights_to_check)
228        } else {
229            // URef is not known
230            false
231        }
232    }
233
234    /// Returns a reference to the map of access rights.
235    pub fn access_rights(&self) -> &BTreeMap<URefAddr, AccessRights> {
236        &self.access_rights
237    }
238
239    /// Consume into access rights.
240    pub fn take_access_rights(self) -> BTreeMap<URefAddr, AccessRights> {
241        self.access_rights
242    }
243
244    /// Grants access to a [`URef`]; unless access was pre-existing.
245    pub fn grant_access(&mut self, uref: URef) -> GrantedAccess {
246        match self.access_rights.entry(uref.addr()) {
247            Entry::Occupied(existing_rights) => {
248                let newly_granted_access_rights =
249                    uref.access_rights().difference(*existing_rights.get());
250                *existing_rights.into_mut() = existing_rights.get().union(uref.access_rights());
251                if newly_granted_access_rights.is_none() {
252                    GrantedAccess::PreExisting
253                } else {
254                    GrantedAccess::Granted {
255                        uref_addr: uref.addr(),
256                        newly_granted_access_rights,
257                    }
258                }
259            }
260            Entry::Vacant(rights) => {
261                rights.insert(uref.access_rights());
262                GrantedAccess::Granted {
263                    uref_addr: uref.addr(),
264                    newly_granted_access_rights: uref.access_rights(),
265                }
266            }
267        }
268    }
269
270    /// Remove access for a given `URef`.
271    pub fn remove_access(&mut self, uref_addr: URefAddr, access_rights: AccessRights) {
272        if let Some(current_access_rights) = self.access_rights.get_mut(&uref_addr) {
273            current_access_rights.remove(access_rights)
274        }
275    }
276}
277
278#[cfg(test)]
279mod tests {
280    use super::*;
281    use crate::UREF_ADDR_LENGTH;
282
283    const ENTITY_HASH: HashAddr = [1u8; 32];
284    const UREF_ADDRESS: [u8; UREF_ADDR_LENGTH] = [1; UREF_ADDR_LENGTH];
285    const UREF_NO_PERMISSIONS: URef = URef::new(UREF_ADDRESS, AccessRights::empty());
286    const UREF_READ: URef = URef::new(UREF_ADDRESS, AccessRights::READ);
287    const UREF_ADD: URef = URef::new(UREF_ADDRESS, AccessRights::ADD);
288    const UREF_WRITE: URef = URef::new(UREF_ADDRESS, AccessRights::WRITE);
289    const UREF_READ_ADD: URef = URef::new(UREF_ADDRESS, AccessRights::READ_ADD);
290    const UREF_READ_ADD_WRITE: URef = URef::new(UREF_ADDRESS, AccessRights::READ_ADD_WRITE);
291
292    fn test_readable(right: AccessRights, is_true: bool) {
293        assert_eq!(right.is_readable(), is_true)
294    }
295
296    #[test]
297    fn test_is_readable() {
298        test_readable(AccessRights::READ, true);
299        test_readable(AccessRights::READ_ADD, true);
300        test_readable(AccessRights::READ_WRITE, true);
301        test_readable(AccessRights::READ_ADD_WRITE, true);
302        test_readable(AccessRights::ADD, false);
303        test_readable(AccessRights::ADD_WRITE, false);
304        test_readable(AccessRights::WRITE, false);
305    }
306
307    fn test_writable(right: AccessRights, is_true: bool) {
308        assert_eq!(right.is_writeable(), is_true)
309    }
310
311    #[test]
312    fn test_is_writable() {
313        test_writable(AccessRights::WRITE, true);
314        test_writable(AccessRights::READ_WRITE, true);
315        test_writable(AccessRights::ADD_WRITE, true);
316        test_writable(AccessRights::READ, false);
317        test_writable(AccessRights::ADD, false);
318        test_writable(AccessRights::READ_ADD, false);
319        test_writable(AccessRights::READ_ADD_WRITE, true);
320    }
321
322    fn test_addable(right: AccessRights, is_true: bool) {
323        assert_eq!(right.is_addable(), is_true)
324    }
325
326    #[test]
327    fn test_is_addable() {
328        test_addable(AccessRights::ADD, true);
329        test_addable(AccessRights::READ_ADD, true);
330        test_addable(AccessRights::READ_WRITE, false);
331        test_addable(AccessRights::ADD_WRITE, true);
332        test_addable(AccessRights::READ, false);
333        test_addable(AccessRights::WRITE, false);
334        test_addable(AccessRights::READ_ADD_WRITE, true);
335    }
336
337    #[test]
338    fn should_check_has_access_rights_to_uref() {
339        let context_rights = ContextAccessRights::new(ENTITY_HASH, vec![UREF_READ_ADD]);
340        assert!(context_rights.has_access_rights_to_uref(&UREF_READ_ADD));
341        assert!(context_rights.has_access_rights_to_uref(&UREF_READ));
342        assert!(context_rights.has_access_rights_to_uref(&UREF_ADD));
343        assert!(context_rights.has_access_rights_to_uref(&UREF_NO_PERMISSIONS));
344    }
345
346    #[test]
347    fn should_check_does_not_have_access_rights_to_uref() {
348        let context_rights = ContextAccessRights::new(ENTITY_HASH, vec![UREF_READ_ADD]);
349        assert!(!context_rights.has_access_rights_to_uref(&UREF_READ_ADD_WRITE));
350        assert!(!context_rights
351            .has_access_rights_to_uref(&URef::new([2; UREF_ADDR_LENGTH], AccessRights::empty())));
352    }
353
354    #[test]
355    fn should_extend_access_rights() {
356        // Start with uref with no permissions.
357        let mut context_rights = ContextAccessRights::new(ENTITY_HASH, vec![UREF_NO_PERMISSIONS]);
358        let mut expected_rights = BTreeMap::new();
359        expected_rights.insert(UREF_ADDRESS, AccessRights::empty());
360        assert_eq!(context_rights.access_rights, expected_rights);
361
362        // Extend with a READ_ADD: should merge to single READ_ADD.
363        context_rights.extend(&[UREF_READ_ADD]);
364        *expected_rights.get_mut(&UREF_ADDRESS).unwrap() = AccessRights::READ_ADD;
365        assert_eq!(context_rights.access_rights, expected_rights);
366
367        // Extend with a READ: should have no observable effect.
368        context_rights.extend(&[UREF_READ]);
369        assert_eq!(context_rights.access_rights, expected_rights);
370
371        // Extend with a WRITE: should merge to single READ_ADD_WRITE.
372        context_rights.extend(&[UREF_WRITE]);
373        *expected_rights.get_mut(&UREF_ADDRESS).unwrap() = AccessRights::READ_ADD_WRITE;
374        assert_eq!(context_rights.access_rights, expected_rights);
375    }
376
377    #[test]
378    fn should_perform_union_of_access_rights_in_new() {
379        let context_rights =
380            ContextAccessRights::new(ENTITY_HASH, vec![UREF_NO_PERMISSIONS, UREF_READ, UREF_ADD]);
381
382        // Expect the three discrete URefs' rights to be unioned into READ_ADD.
383        let mut expected_rights = BTreeMap::new();
384        expected_rights.insert(UREF_ADDRESS, AccessRights::READ_ADD);
385        assert_eq!(context_rights.access_rights, expected_rights);
386    }
387
388    #[test]
389    fn should_grant_access_rights() {
390        let mut context_rights = ContextAccessRights::new(ENTITY_HASH, vec![UREF_READ_ADD]);
391        let granted_access = context_rights.grant_access(UREF_READ);
392        assert_eq!(granted_access, GrantedAccess::PreExisting);
393        let granted_access = context_rights.grant_access(UREF_READ_ADD_WRITE);
394        assert_eq!(
395            granted_access,
396            GrantedAccess::Granted {
397                uref_addr: UREF_ADDRESS,
398                newly_granted_access_rights: AccessRights::WRITE
399            }
400        );
401        assert!(context_rights.has_access_rights_to_uref(&UREF_READ_ADD_WRITE));
402        let new_uref = URef::new([3; 32], AccessRights::all());
403        let granted_access = context_rights.grant_access(new_uref);
404        assert_eq!(
405            granted_access,
406            GrantedAccess::Granted {
407                uref_addr: new_uref.addr(),
408                newly_granted_access_rights: AccessRights::all()
409            }
410        );
411        assert!(context_rights.has_access_rights_to_uref(&new_uref));
412    }
413
414    #[test]
415    fn should_remove_access_rights() {
416        let mut context_rights = ContextAccessRights::new(ENTITY_HASH, vec![UREF_READ_ADD_WRITE]);
417        assert!(context_rights.has_access_rights_to_uref(&UREF_READ_ADD_WRITE));
418
419        // Strip write access from the context rights.
420        context_rights.remove_access(UREF_ADDRESS, AccessRights::WRITE);
421        assert!(
422            !context_rights.has_access_rights_to_uref(&UREF_READ_ADD_WRITE),
423            "Write access should have been removed"
424        );
425
426        // Strip the access again to ensure that the bit is not flipped back.
427        context_rights.remove_access(UREF_ADDRESS, AccessRights::WRITE);
428        assert!(
429            !context_rights.has_access_rights_to_uref(&UREF_READ_ADD_WRITE),
430            "Write access should not have been granted back"
431        );
432        assert!(
433            context_rights.has_access_rights_to_uref(&UREF_READ_ADD),
434            "Read and add access should be preserved."
435        );
436
437        // Strip both read and add access from the context rights.
438        context_rights.remove_access(UREF_ADDRESS, AccessRights::READ_ADD);
439        assert!(
440            !context_rights.has_access_rights_to_uref(&UREF_READ_ADD),
441            "Read and add access should have been removed"
442        );
443        assert!(
444            context_rights.has_access_rights_to_uref(&UREF_NO_PERMISSIONS),
445            "The access rights should be empty"
446        );
447    }
448}