Skip to main content

dpp/identity/identity_public_key/
security_level.rs

1use bincode::{Decode, DecodeUntrusted, Encode};
2#[cfg(feature = "cbor")]
3use ciborium::value::Value as CborValue;
4
5use serde_repr::{Deserialize_repr, Serialize_repr};
6
7use crate::consensus::basic::data_contract::UnknownSecurityLevelError;
8use crate::consensus::basic::BasicError;
9use crate::consensus::ConsensusError;
10use crate::ProtocolError;
11use std::convert::TryFrom;
12
13#[repr(u8)]
14#[derive(
15    Debug,
16    PartialEq,
17    Eq,
18    Clone,
19    Copy,
20    Hash,
21    Serialize_repr,
22    Deserialize_repr,
23    PartialOrd,
24    Ord,
25    Encode,
26    Decode,
27    Default,
28    strum::EnumIter,
29    DecodeUntrusted,
30)]
31pub enum SecurityLevel {
32    MASTER = 0,
33    CRITICAL = 1,
34    #[default]
35    HIGH = 2,
36    MEDIUM = 3,
37}
38
39impl From<SecurityLevel> for [u8; 1] {
40    fn from(security_level: SecurityLevel) -> Self {
41        [security_level as u8]
42    }
43}
44
45impl From<SecurityLevel> for &'static [u8; 1] {
46    fn from(security_level: SecurityLevel) -> Self {
47        match security_level {
48            SecurityLevel::MASTER => &[0],
49            SecurityLevel::CRITICAL => &[1],
50            SecurityLevel::HIGH => &[2],
51            SecurityLevel::MEDIUM => &[3],
52        }
53    }
54}
55
56#[cfg(feature = "cbor")]
57impl Into<CborValue> for SecurityLevel {
58    fn into(self) -> CborValue {
59        CborValue::from(self as u128)
60    }
61}
62
63impl TryFrom<u8> for SecurityLevel {
64    type Error = ProtocolError;
65    fn try_from(value: u8) -> Result<Self, ProtocolError> {
66        match value {
67            0 => Ok(Self::MASTER),
68            1 => Ok(Self::CRITICAL),
69            2 => Ok(Self::HIGH),
70            3 => Ok(Self::MEDIUM),
71            value => Err(ProtocolError::ConsensusError(
72                ConsensusError::BasicError(BasicError::UnknownSecurityLevelError(
73                    UnknownSecurityLevelError::new(vec![0, 1, 2, 3], value),
74                ))
75                .into(),
76            )),
77        }
78    }
79}
80
81impl SecurityLevel {
82    /// The full range of security levels
83    pub fn full_range() -> [SecurityLevel; 4] {
84        [Self::MASTER, Self::CRITICAL, Self::HIGH, Self::MEDIUM]
85    }
86    pub fn last() -> SecurityLevel {
87        Self::MEDIUM
88    }
89    pub fn lowest_level() -> SecurityLevel {
90        Self::MEDIUM
91    }
92    pub fn highest_level() -> SecurityLevel {
93        Self::MASTER
94    }
95    pub fn stronger_security_than(self: SecurityLevel, rhs: SecurityLevel) -> bool {
96        // Example:
97        // self: High 2 rhs: Master 0
98        // Master has a stronger security level than high
99        // We expect False
100        // High < Master
101        // 2 < 0 <=> false
102        (self as u8) < (rhs as u8)
103    }
104
105    pub fn stronger_or_equal_security_than(self: SecurityLevel, rhs: SecurityLevel) -> bool {
106        (self as u8) <= (rhs as u8)
107    }
108}
109
110impl std::fmt::Display for SecurityLevel {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        write!(f, "{self:?}")
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    // -- TryFrom<u8> valid --
121    #[test]
122    fn test_security_level_try_from_u8_all_valid() {
123        assert_eq!(SecurityLevel::try_from(0u8).unwrap(), SecurityLevel::MASTER);
124        assert_eq!(
125            SecurityLevel::try_from(1u8).unwrap(),
126            SecurityLevel::CRITICAL
127        );
128        assert_eq!(SecurityLevel::try_from(2u8).unwrap(), SecurityLevel::HIGH);
129        assert_eq!(SecurityLevel::try_from(3u8).unwrap(), SecurityLevel::MEDIUM);
130    }
131
132    // -- TryFrom<u8> invalid returns UnknownSecurityLevelError --
133    #[test]
134    fn test_security_level_try_from_u8_invalid_is_consensus_error() {
135        let err = SecurityLevel::try_from(4u8).unwrap_err();
136        // Confirm it is a ProtocolError::ConsensusError wrapping BasicError::UnknownSecurityLevelError.
137        match err {
138            ProtocolError::ConsensusError(ce) => match *ce {
139                ConsensusError::BasicError(BasicError::UnknownSecurityLevelError(_)) => {}
140                other => panic!("unexpected inner consensus error: {:?}", other),
141            },
142            other => panic!("expected ProtocolError::ConsensusError, got {:?}", other),
143        }
144    }
145
146    #[test]
147    fn test_security_level_try_from_u8_invalid_255() {
148        assert!(SecurityLevel::try_from(255u8).is_err());
149    }
150
151    // -- From<SecurityLevel> for [u8; 1] (owned) --
152    #[test]
153    fn test_security_level_to_owned_byte_array() {
154        let arr: [u8; 1] = SecurityLevel::MASTER.into();
155        assert_eq!(arr, [0]);
156        let arr: [u8; 1] = SecurityLevel::CRITICAL.into();
157        assert_eq!(arr, [1]);
158        let arr: [u8; 1] = SecurityLevel::HIGH.into();
159        assert_eq!(arr, [2]);
160        let arr: [u8; 1] = SecurityLevel::MEDIUM.into();
161        assert_eq!(arr, [3]);
162    }
163
164    // -- From<SecurityLevel> for &'static [u8; 1] --
165    #[test]
166    fn test_security_level_to_static_byte_ref_all_variants() {
167        let r: &'static [u8; 1] = SecurityLevel::MASTER.into();
168        assert_eq!(r, &[0u8]);
169        let r: &'static [u8; 1] = SecurityLevel::CRITICAL.into();
170        assert_eq!(r, &[1u8]);
171        let r: &'static [u8; 1] = SecurityLevel::HIGH.into();
172        assert_eq!(r, &[2u8]);
173        let r: &'static [u8; 1] = SecurityLevel::MEDIUM.into();
174        assert_eq!(r, &[3u8]);
175    }
176
177    // -- Display --
178    #[test]
179    fn test_security_level_display_matches_debug_form() {
180        assert_eq!(format!("{}", SecurityLevel::MASTER), "MASTER");
181        assert_eq!(format!("{}", SecurityLevel::CRITICAL), "CRITICAL");
182        assert_eq!(format!("{}", SecurityLevel::HIGH), "HIGH");
183        assert_eq!(format!("{}", SecurityLevel::MEDIUM), "MEDIUM");
184    }
185
186    // -- Default is HIGH --
187    #[test]
188    fn test_security_level_default_is_high() {
189        assert_eq!(SecurityLevel::default(), SecurityLevel::HIGH);
190    }
191
192    // -- full_range, last, lowest_level, highest_level --
193    #[test]
194    fn test_security_level_full_range() {
195        let r = SecurityLevel::full_range();
196        assert_eq!(r.len(), 4);
197        assert_eq!(
198            r,
199            [
200                SecurityLevel::MASTER,
201                SecurityLevel::CRITICAL,
202                SecurityLevel::HIGH,
203                SecurityLevel::MEDIUM,
204            ]
205        );
206    }
207
208    #[test]
209    fn test_security_level_last_and_lowest_are_medium() {
210        assert_eq!(SecurityLevel::last(), SecurityLevel::MEDIUM);
211        assert_eq!(SecurityLevel::lowest_level(), SecurityLevel::MEDIUM);
212    }
213
214    #[test]
215    fn test_security_level_highest_is_master() {
216        assert_eq!(SecurityLevel::highest_level(), SecurityLevel::MASTER);
217    }
218
219    // -- stronger_security_than: strict < --
220    #[test]
221    fn test_stronger_security_than_master_vs_medium() {
222        // Master (0) is stronger than Medium (3) because 0 < 3.
223        assert!(SecurityLevel::MASTER.stronger_security_than(SecurityLevel::MEDIUM));
224        // Medium is NOT stronger than Master.
225        assert!(!SecurityLevel::MEDIUM.stronger_security_than(SecurityLevel::MASTER));
226    }
227
228    #[test]
229    fn test_stronger_security_than_is_not_reflexive() {
230        // A level is not strictly stronger than itself.
231        assert!(!SecurityLevel::HIGH.stronger_security_than(SecurityLevel::HIGH));
232        assert!(!SecurityLevel::MASTER.stronger_security_than(SecurityLevel::MASTER));
233    }
234
235    #[test]
236    fn test_stronger_security_than_full_matrix() {
237        let all = SecurityLevel::full_range();
238        for (i, a) in all.iter().enumerate() {
239            for (j, b) in all.iter().enumerate() {
240                // full_range is ordered strongest -> weakest, so index i < j iff a is stronger.
241                assert_eq!(a.stronger_security_than(*b), i < j);
242            }
243        }
244    }
245
246    // -- stronger_or_equal_security_than --
247    #[test]
248    fn test_stronger_or_equal_security_than_reflexive() {
249        for lvl in SecurityLevel::full_range() {
250            assert!(lvl.stronger_or_equal_security_than(lvl));
251        }
252    }
253
254    #[test]
255    fn test_stronger_or_equal_security_than_strict() {
256        assert!(SecurityLevel::MASTER.stronger_or_equal_security_than(SecurityLevel::HIGH));
257        assert!(!SecurityLevel::HIGH.stronger_or_equal_security_than(SecurityLevel::MASTER));
258    }
259
260    // -- Ordering derives --
261    #[test]
262    fn test_security_level_ordering_master_lt_critical_lt_high_lt_medium() {
263        assert!(SecurityLevel::MASTER < SecurityLevel::CRITICAL);
264        assert!(SecurityLevel::CRITICAL < SecurityLevel::HIGH);
265        assert!(SecurityLevel::HIGH < SecurityLevel::MEDIUM);
266    }
267
268    // -- round-trip u8 -> SecurityLevel -> u8 --
269    #[test]
270    fn test_security_level_round_trip_u8() {
271        for v in 0u8..=3 {
272            let lvl = SecurityLevel::try_from(v).unwrap();
273            assert_eq!(lvl as u8, v);
274        }
275    }
276}