dpp/identity/identity_public_key/
purpose.rs1use crate::identity::Purpose::{
2 AUTHENTICATION, DECRYPTION, ENCRYPTION, OWNER, SYSTEM, TRANSFER, VOTING,
3};
4use anyhow::bail;
5use bincode::{Decode, DecodeUntrusted, Encode};
6#[cfg(feature = "cbor")]
7use ciborium::value::Value as CborValue;
8use serde_repr::{Deserialize_repr, Serialize_repr};
9use std::convert::TryFrom;
10
11#[repr(u8)]
12#[derive(
13 Debug,
14 PartialEq,
15 Eq,
16 Clone,
17 Copy,
18 Hash,
19 Serialize_repr,
20 Deserialize_repr,
21 Ord,
22 PartialOrd,
23 Encode,
24 Decode,
25 Default,
26 strum::EnumIter,
27 DecodeUntrusted,
28)]
29pub enum Purpose {
30 #[default]
32 AUTHENTICATION = 0,
33 ENCRYPTION = 1,
35 DECRYPTION = 2,
37 TRANSFER = 3,
40 SYSTEM = 4,
42 VOTING = 5,
44 OWNER = 6,
46}
47
48impl From<Purpose> for [u8; 1] {
49 fn from(purpose: Purpose) -> Self {
50 [purpose as u8]
51 }
52}
53
54impl From<Purpose> for &'static [u8; 1] {
55 fn from(purpose: Purpose) -> Self {
56 match purpose {
57 AUTHENTICATION => &[0],
58 ENCRYPTION => &[1],
59 DECRYPTION => &[2],
60 TRANSFER => &[3],
61 SYSTEM => &[4],
62 VOTING => &[5],
63 OWNER => &[6],
64 }
65 }
66}
67
68impl TryFrom<u8> for Purpose {
69 type Error = anyhow::Error;
70 fn try_from(value: u8) -> Result<Self, Self::Error> {
71 match value {
72 0 => Ok(AUTHENTICATION),
73 1 => Ok(ENCRYPTION),
74 2 => Ok(DECRYPTION),
75 3 => Ok(TRANSFER),
76 4 => Ok(SYSTEM),
77 5 => Ok(VOTING),
78 6 => Ok(OWNER),
79 value => bail!("unrecognized purpose: {}", value),
80 }
81 }
82}
83
84impl TryFrom<i32> for Purpose {
85 type Error = anyhow::Error;
86 fn try_from(value: i32) -> Result<Self, Self::Error> {
87 match value {
88 0 => Ok(AUTHENTICATION),
89 1 => Ok(ENCRYPTION),
90 2 => Ok(DECRYPTION),
91 3 => Ok(TRANSFER),
92 4 => Ok(SYSTEM),
93 5 => Ok(VOTING),
94 6 => Ok(OWNER),
95 value => bail!("unrecognized purpose: {}", value),
96 }
97 }
98}
99
100#[cfg(feature = "cbor")]
101impl Into<CborValue> for Purpose {
102 fn into(self) -> CborValue {
103 CborValue::from(self as u128)
104 }
105}
106impl std::fmt::Display for Purpose {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 write!(f, "{self:?}")
109 }
110}
111
112impl Purpose {
113 pub fn full_range() -> [Purpose; 6] {
115 [
116 AUTHENTICATION,
117 ENCRYPTION,
118 DECRYPTION,
119 TRANSFER,
120 VOTING,
121 OWNER,
122 ]
123 }
124 pub fn searchable_purposes() -> [Purpose; 3] {
126 [AUTHENTICATION, TRANSFER, VOTING]
127 }
128 pub fn encryption_decryption() -> [Purpose; 2] {
130 [ENCRYPTION, DECRYPTION]
131 }
132
133 pub fn wire_name(&self) -> &'static str {
136 match self {
137 AUTHENTICATION => "authentication",
138 ENCRYPTION => "encryption",
139 DECRYPTION => "decryption",
140 TRANSFER => "transfer",
141 SYSTEM => "system",
142 VOTING => "voting",
143 OWNER => "owner",
144 }
145 }
146
147 pub fn from_wire_name(name: &str) -> Option<Self> {
149 match name {
150 "authentication" => Some(AUTHENTICATION),
151 "encryption" => Some(ENCRYPTION),
152 "decryption" => Some(DECRYPTION),
153 "transfer" => Some(TRANSFER),
154 "system" => Some(SYSTEM),
155 "voting" => Some(VOTING),
156 "owner" => Some(OWNER),
157 _ => None,
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
168 fn test_purpose_try_from_u8_valid_all_variants() {
169 assert_eq!(Purpose::try_from(0u8).unwrap(), AUTHENTICATION);
170 assert_eq!(Purpose::try_from(1u8).unwrap(), ENCRYPTION);
171 assert_eq!(Purpose::try_from(2u8).unwrap(), DECRYPTION);
172 assert_eq!(Purpose::try_from(3u8).unwrap(), TRANSFER);
173 assert_eq!(Purpose::try_from(4u8).unwrap(), SYSTEM);
174 assert_eq!(Purpose::try_from(5u8).unwrap(), VOTING);
175 assert_eq!(Purpose::try_from(6u8).unwrap(), OWNER);
176 }
177
178 #[test]
180 fn test_purpose_try_from_u8_invalid() {
181 assert!(Purpose::try_from(7u8).is_err());
182 assert!(Purpose::try_from(255u8).is_err());
183 }
184
185 #[test]
187 fn test_purpose_try_from_i32_valid_all_variants() {
188 assert_eq!(Purpose::try_from(0i32).unwrap(), AUTHENTICATION);
189 assert_eq!(Purpose::try_from(1i32).unwrap(), ENCRYPTION);
190 assert_eq!(Purpose::try_from(2i32).unwrap(), DECRYPTION);
191 assert_eq!(Purpose::try_from(3i32).unwrap(), TRANSFER);
192 assert_eq!(Purpose::try_from(4i32).unwrap(), SYSTEM);
193 assert_eq!(Purpose::try_from(5i32).unwrap(), VOTING);
194 assert_eq!(Purpose::try_from(6i32).unwrap(), OWNER);
195 }
196
197 #[test]
198 fn test_purpose_try_from_i32_invalid() {
199 assert!(Purpose::try_from(-1i32).is_err());
200 assert!(Purpose::try_from(7i32).is_err());
201 assert!(Purpose::try_from(1_000_000i32).is_err());
202 }
203
204 #[test]
206 fn test_purpose_to_owned_byte_array() {
207 let arr: [u8; 1] = AUTHENTICATION.into();
208 assert_eq!(arr, [0]);
209 let arr: [u8; 1] = OWNER.into();
210 assert_eq!(arr, [6]);
211 let arr: [u8; 1] = SYSTEM.into();
212 assert_eq!(arr, [4]);
213 }
214
215 #[test]
217 fn test_purpose_to_static_byte_ref_all_variants() {
218 let r: &'static [u8; 1] = AUTHENTICATION.into();
219 assert_eq!(r, &[0u8]);
220 let r: &'static [u8; 1] = ENCRYPTION.into();
221 assert_eq!(r, &[1u8]);
222 let r: &'static [u8; 1] = DECRYPTION.into();
223 assert_eq!(r, &[2u8]);
224 let r: &'static [u8; 1] = TRANSFER.into();
225 assert_eq!(r, &[3u8]);
226 let r: &'static [u8; 1] = SYSTEM.into();
227 assert_eq!(r, &[4u8]);
228 let r: &'static [u8; 1] = VOTING.into();
229 assert_eq!(r, &[5u8]);
230 let r: &'static [u8; 1] = OWNER.into();
231 assert_eq!(r, &[6u8]);
232 }
233
234 #[test]
236 fn test_purpose_display_matches_debug_form() {
237 assert_eq!(format!("{}", AUTHENTICATION), "AUTHENTICATION");
238 assert_eq!(format!("{}", ENCRYPTION), "ENCRYPTION");
239 assert_eq!(format!("{}", DECRYPTION), "DECRYPTION");
240 assert_eq!(format!("{}", TRANSFER), "TRANSFER");
241 assert_eq!(format!("{}", SYSTEM), "SYSTEM");
242 assert_eq!(format!("{}", VOTING), "VOTING");
243 assert_eq!(format!("{}", OWNER), "OWNER");
244 }
245
246 #[test]
248 fn test_purpose_default_is_authentication() {
249 assert_eq!(Purpose::default(), AUTHENTICATION);
250 }
251
252 #[test]
254 fn test_purpose_full_range_contents() {
255 let full = Purpose::full_range();
257 assert_eq!(full.len(), 6);
258 assert!(full.contains(&AUTHENTICATION));
259 assert!(full.contains(&ENCRYPTION));
260 assert!(full.contains(&DECRYPTION));
261 assert!(full.contains(&TRANSFER));
262 assert!(full.contains(&VOTING));
263 assert!(full.contains(&OWNER));
264 assert!(!full.contains(&SYSTEM));
265 }
266
267 #[test]
268 fn test_purpose_searchable_purposes_contents() {
269 let searchable = Purpose::searchable_purposes();
270 assert_eq!(searchable.len(), 3);
271 assert_eq!(searchable, [AUTHENTICATION, TRANSFER, VOTING]);
272 }
273
274 #[test]
275 fn test_purpose_encryption_decryption_contents() {
276 let ed = Purpose::encryption_decryption();
277 assert_eq!(ed.len(), 2);
278 assert_eq!(ed, [ENCRYPTION, DECRYPTION]);
279 }
280
281 #[test]
283 fn test_purpose_round_trip_u8() {
284 for val in 0u8..=6 {
285 let p = Purpose::try_from(val).unwrap();
286 assert_eq!(p as u8, val);
287 }
288 }
289
290 #[test]
292 fn should_round_trip_every_purpose_through_its_wire_name() {
293 for purpose in [
294 AUTHENTICATION,
295 ENCRYPTION,
296 DECRYPTION,
297 TRANSFER,
298 SYSTEM,
299 VOTING,
300 OWNER,
301 ] {
302 let name = purpose.wire_name();
303 assert_eq!(name, format!("{purpose:?}").to_lowercase());
304 assert_eq!(Purpose::from_wire_name(name), Some(purpose));
305 }
306 assert_eq!(Purpose::from_wire_name("AUTHENTICATION"), None);
307 assert_eq!(Purpose::from_wire_name("signing"), None);
308 }
309
310 #[test]
312 fn test_purpose_ordering_matches_discriminant() {
313 assert!(AUTHENTICATION < ENCRYPTION);
314 assert!(ENCRYPTION < DECRYPTION);
315 assert!(DECRYPTION < TRANSFER);
316 assert!(TRANSFER < SYSTEM);
317 assert!(SYSTEM < VOTING);
318 assert!(VOTING < OWNER);
319 }
320}