Skip to main content

dpp/identity/identity_public_key/
security_level.rs

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