Skip to main content

dpp/identity/identity_public_key/
mod.rs

1#![allow(clippy::from_over_into)]
2
3use crate::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0;
4use crate::identity::identity_public_key::v0::IdentityPublicKeyV0;
5#[cfg(feature = "json-conversion")]
6use crate::serialization::JsonConvertible;
7#[cfg(feature = "value-conversion")]
8use crate::serialization::ValueConvertible;
9use bincode::{Decode, Encode};
10use derive_more::From;
11use serde::{Deserialize, Serialize};
12
13mod key_type;
14mod purpose;
15mod security_level;
16pub use key_type::KeyType;
17pub use purpose::Purpose;
18pub use security_level::SecurityLevel;
19pub mod accessors;
20pub mod conversion;
21pub mod fields;
22pub mod v0;
23use crate::version::PlatformVersion;
24use crate::ProtocolError;
25pub use fields::*;
26use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize};
27
28pub mod methods;
29pub use methods::*;
30pub mod contract_bounds;
31#[cfg(feature = "random-public-keys")]
32mod random;
33
34pub type KeyID = u32;
35pub type KeyCount = KeyID;
36pub type TimestampMillis = u64;
37
38#[derive(
39    Debug,
40    Clone,
41    Eq,
42    PartialEq,
43    Serialize,
44    Deserialize,
45    Encode,
46    Decode,
47    PlatformDeserialize,
48    PlatformSerialize,
49    From,
50    Hash,
51    Ord,
52    PartialOrd,
53)]
54#[platform_serialize(limit = 2000, unversioned)] //This is not platform versioned automatically
55#[cfg_attr(feature = "value-conversion", derive(ValueConvertible))]
56#[serde(tag = "$formatVersion")]
57pub enum IdentityPublicKey {
58    #[serde(rename = "0")]
59    V0(IdentityPublicKeyV0),
60}
61
62#[cfg(feature = "json-conversion")]
63impl JsonConvertible for IdentityPublicKey {}
64
65#[cfg(all(
66    test,
67    feature = "json-conversion",
68    feature = "value-conversion",
69    feature = "serde-conversion"
70))]
71mod json_convertible_tests {
72    use super::*;
73    use crate::identity::identity_public_key::v0::IdentityPublicKeyV0;
74    use platform_value::{platform_value, BinaryData, Value};
75    use serde_json::json;
76
77    fn fixture() -> IdentityPublicKey {
78        IdentityPublicKey::V0(IdentityPublicKeyV0 {
79            id: 9,
80            key_type: KeyType::ECDSA_HASH160,
81            purpose: Purpose::TRANSFER,
82            security_level: SecurityLevel::CRITICAL,
83            contract_bounds: None,
84            read_only: true,
85            data: BinaryData::new(vec![0x55; 20]),
86            disabled_at: Some(1_700_000_000_000),
87        })
88    }
89
90    #[test]
91    fn json_round_trip_with_full_wire_shape() {
92        use crate::serialization::JsonConvertible;
93        let original = fixture();
94        let json = original.to_json().expect("to_json");
95        // Internally-tagged enum (`tag = "$formatVersion"`); inner V0 has
96        // `rename_all = "camelCase"`, plus the explicit `serde(rename = "type")`
97        // on `key_type`. Sized-int fields with JSON loss:
98        // - `id`: KeyID = u32 (erased to JSON Number)
99        // - `key_type`/`purpose`/`security_level`: `#[repr(u8)]` enums with
100        //   `Serialize_repr` -> raw u8 discriminants
101        // - `disabled_at`: Option<u64>
102        // `BinaryData` is base64-encoded in JSON (HR).
103        // KeyType::ECDSA_HASH160 = 2, Purpose::TRANSFER = 3,
104        // SecurityLevel::CRITICAL = 1.
105        assert_eq!(
106            json,
107            json!({
108                "$formatVersion": "0",
109                "id": 9,
110                "purpose": 3,
111                "securityLevel": 1,
112                "contractBounds": serde_json::Value::Null,
113                "type": 2,
114                "readOnly": true,
115                "data": "VVVVVVVVVVVVVVVVVVVVVVVVVVU=",
116                "disabledAt": 1_700_000_000_000u64,
117            })
118        );
119        let recovered = IdentityPublicKey::from_json(json).expect("from_json");
120        assert_eq!(original, recovered);
121    }
122
123    #[test]
124    fn value_round_trip_with_full_wire_shape() {
125        use crate::serialization::ValueConvertible;
126        let original = fixture();
127        let value = original.to_object().expect("to_object");
128        // Explicit suffixes lock the typed-int variants:
129        // - `9u32` -> `Value::U32` for the KeyID
130        // - `4u8` / `1u8` -> `Value::U8` for the repr(u8) enums
131        // - `1_700_000_000_000u64` -> `Value::U64` for the timestamp
132        // `BinaryData` becomes a sized-bytes variant in non-HR — for a 20-byte
133        // payload it is detected as `Value::Bytes20([u8; 20])`, not the generic
134        // `Value::Bytes(Vec<u8>)`. (`platform_value` collapses fixed sizes
135        // 20/32/36 to typed variants for round-trip stability.)
136        assert_eq!(
137            value,
138            platform_value!({
139                "$formatVersion": "0",
140                "id": 9u32,
141                "purpose": 3u8,
142                "securityLevel": 1u8,
143                "contractBounds": Value::Null,
144                "type": 2u8,
145                "readOnly": true,
146                "data": Value::Bytes20([0x55; 20]),
147                "disabledAt": 1_700_000_000_000u64,
148            })
149        );
150        let recovered = IdentityPublicKey::from_object(value).expect("from_object");
151        assert_eq!(original, recovered);
152    }
153}
154
155impl IdentityPublicKey {
156    /// Checks if public key security level is MASTER
157    pub fn is_master(&self) -> bool {
158        self.security_level() == SecurityLevel::MASTER
159    }
160
161    /// Generates an identity public key with the maximum possible size based on the platform version.
162    ///
163    /// This method constructs a key of the largest possible size for the given platform version.
164    /// This can be useful for stress testing or benchmarking purposes.
165    ///
166    /// # Parameters
167    ///
168    /// * `id`: The `KeyID` for the generated key.
169    /// * `platform_version`: The platform version which determines the structure of the identity key.
170    ///
171    /// # Returns
172    ///
173    /// * `Self`: An instance of the `IdentityPublicKey` struct.
174    ///
175    pub fn max_possible_size_key(
176        id: KeyID,
177        platform_version: &PlatformVersion,
178    ) -> Result<Self, ProtocolError> {
179        match platform_version
180            .dpp
181            .identity_versions
182            .identity_key_structure_version
183        {
184            0 => Ok(IdentityPublicKeyV0::max_possible_size_key(id).into()),
185            version => Err(ProtocolError::UnknownVersionMismatch {
186                method: "IdentityPublicKey::max_possible_size_key".to_string(),
187                known_versions: vec![0],
188                received: version,
189            }),
190        }
191    }
192
193    pub fn default_versioned(platform_version: &PlatformVersion) -> Result<Self, ProtocolError> {
194        match platform_version
195            .dpp
196            .identity_versions
197            .identity_key_structure_version
198        {
199            0 => Ok(IdentityPublicKeyV0::default().into()),
200            version => Err(ProtocolError::UnknownVersionMismatch {
201                method: "IdentityPublicKey::default_versioned".to_string(),
202                known_versions: vec![0],
203                received: version,
204            }),
205        }
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use crate::identity::identity_public_key::accessors::v0::{
212        IdentityPublicKeyGettersV0, IdentityPublicKeySettersV0,
213    };
214    use crate::identity::identity_public_key::contract_bounds::ContractBounds;
215    use crate::identity::identity_public_key::v0::IdentityPublicKeyV0;
216    use crate::identity::{IdentityPublicKey, KeyType, Purpose, SecurityLevel};
217    use crate::serialization::{PlatformDeserializable, PlatformSerializable};
218    use platform_value::{BinaryData, Identifier};
219    use platform_version::version::LATEST_PLATFORM_VERSION;
220    use rand::SeedableRng;
221
222    #[test]
223    fn test_identity_key_serialization_deserialization() {
224        let mut rng = rand::rngs::StdRng::from_entropy();
225        let key: IdentityPublicKey =
226            IdentityPublicKeyV0::random_ecdsa_master_authentication_key_with_rng(
227                1,
228                &mut rng,
229                LATEST_PLATFORM_VERSION,
230            )
231            .expect("expected a random key")
232            .0
233            .into();
234        let serialized = key.serialize_to_bytes().expect("expected to serialize key");
235        let unserialized: IdentityPublicKey =
236            PlatformDeserializable::deserialize_from_bytes(serialized.as_slice())
237                .expect("expected to deserialize key");
238        assert_eq!(key, unserialized)
239    }
240
241    // -- is_master --
242
243    fn make_key_v0(security_level: SecurityLevel) -> IdentityPublicKey {
244        IdentityPublicKey::V0(IdentityPublicKeyV0 {
245            id: 42,
246            purpose: Purpose::AUTHENTICATION,
247            security_level,
248            contract_bounds: None,
249            key_type: KeyType::ECDSA_SECP256K1,
250            data: BinaryData::new(vec![0u8; 33]),
251            read_only: false,
252            disabled_at: None,
253        })
254    }
255
256    #[test]
257    fn test_is_master_true_when_security_level_master() {
258        let key = make_key_v0(SecurityLevel::MASTER);
259        assert!(key.is_master());
260    }
261
262    #[test]
263    fn test_is_master_false_for_other_levels() {
264        assert!(!make_key_v0(SecurityLevel::CRITICAL).is_master());
265        assert!(!make_key_v0(SecurityLevel::HIGH).is_master());
266        assert!(!make_key_v0(SecurityLevel::MEDIUM).is_master());
267    }
268
269    // -- max_possible_size_key --
270
271    #[test]
272    fn test_max_possible_size_key_v0_returns_bls_sized_data() {
273        // BLS12_381 has the largest default size (48 bytes).
274        let key = IdentityPublicKey::max_possible_size_key(123, LATEST_PLATFORM_VERSION)
275            .expect("max_possible_size_key should work for v0");
276        assert_eq!(key.id(), 123);
277        assert_eq!(key.purpose(), Purpose::AUTHENTICATION);
278        assert_eq!(key.security_level(), SecurityLevel::MASTER);
279        assert!(!key.read_only());
280        assert!(key.contract_bounds().is_none());
281        assert!(key.disabled_at().is_none());
282        // data should be default_size() bytes filled with 0xff.
283        assert_eq!(key.data().len(), key.key_type().default_size());
284        assert!(key.data().as_slice().iter().all(|b| *b == 0xff));
285    }
286
287    // -- default_versioned --
288
289    #[test]
290    fn test_default_versioned_returns_v0_default() {
291        let key = IdentityPublicKey::default_versioned(LATEST_PLATFORM_VERSION)
292            .expect("default_versioned should work for v0");
293        // The V0 Default should have id=0 and default purpose/security_level/key_type.
294        assert_eq!(key.id(), 0);
295        assert_eq!(key.purpose(), Purpose::default());
296        assert_eq!(key.security_level(), SecurityLevel::default());
297        assert_eq!(key.key_type(), KeyType::default());
298    }
299
300    // -- accessors on the IdentityPublicKey enum wrapper --
301
302    #[test]
303    fn test_wrapper_getters_delegate_to_v0() {
304        let inner = IdentityPublicKeyV0 {
305            id: 7,
306            purpose: Purpose::TRANSFER,
307            security_level: SecurityLevel::CRITICAL,
308            contract_bounds: Some(ContractBounds::SingleContract {
309                id: Identifier::from([0x01u8; 32]),
310            }),
311            key_type: KeyType::BLS12_381,
312            data: BinaryData::new(vec![2u8; 48]),
313            read_only: true,
314            disabled_at: Some(1_700_000_000_000),
315        };
316        let key = IdentityPublicKey::V0(inner.clone());
317        assert_eq!(key.id(), 7);
318        assert_eq!(key.purpose(), Purpose::TRANSFER);
319        assert_eq!(key.security_level(), SecurityLevel::CRITICAL);
320        assert_eq!(key.key_type(), KeyType::BLS12_381);
321        assert!(key.read_only());
322        assert_eq!(key.data().as_slice(), &vec![2u8; 48][..]);
323        assert_eq!(key.disabled_at(), Some(1_700_000_000_000));
324        assert!(key.is_disabled());
325        assert!(key.contract_bounds().is_some());
326
327        // data_owned returns the underlying data.
328        let cloned = key.clone();
329        assert_eq!(cloned.data_owned().as_slice(), &vec![2u8; 48][..]);
330    }
331
332    #[test]
333    fn test_wrapper_setters_delegate_to_v0() {
334        let mut key = make_key_v0(SecurityLevel::HIGH);
335        key.set_id(99);
336        key.set_purpose(Purpose::ENCRYPTION);
337        key.set_security_level(SecurityLevel::CRITICAL);
338        key.set_key_type(KeyType::BLS12_381);
339        key.set_read_only(true);
340        key.set_data(BinaryData::new(vec![0xABu8; 48]));
341        key.set_disabled_at(1234);
342
343        assert_eq!(key.id(), 99);
344        assert_eq!(key.purpose(), Purpose::ENCRYPTION);
345        assert_eq!(key.security_level(), SecurityLevel::CRITICAL);
346        assert_eq!(key.key_type(), KeyType::BLS12_381);
347        assert!(key.read_only());
348        assert_eq!(key.data().as_slice(), &vec![0xABu8; 48][..]);
349        assert_eq!(key.disabled_at(), Some(1234));
350        assert!(key.is_disabled());
351
352        key.remove_disabled_at();
353        assert_eq!(key.disabled_at(), None);
354        assert!(!key.is_disabled());
355    }
356
357    // -- From<IdentityPublicKeyV0> for IdentityPublicKey --
358    #[test]
359    fn test_from_v0_into_wrapper() {
360        let v0 = IdentityPublicKeyV0 {
361            id: 5,
362            purpose: Purpose::VOTING,
363            security_level: SecurityLevel::MEDIUM,
364            contract_bounds: None,
365            key_type: KeyType::ECDSA_HASH160,
366            data: BinaryData::new(vec![0u8; 20]),
367            read_only: false,
368            disabled_at: None,
369        };
370        let wrapped: IdentityPublicKey = v0.clone().into();
371        assert_eq!(wrapped.id(), 5);
372        assert_eq!(wrapped.purpose(), Purpose::VOTING);
373        assert_eq!(wrapped.security_level(), SecurityLevel::MEDIUM);
374        // Equality on the enum variant.
375        assert_eq!(wrapped, IdentityPublicKey::V0(v0));
376    }
377
378    // -- IdentityPublicKeyV0::default basic invariants --
379    #[test]
380    fn test_v0_default_fields() {
381        let v0 = IdentityPublicKeyV0::default();
382        assert_eq!(v0.id, 0);
383        assert_eq!(v0.purpose, Purpose::default());
384        assert_eq!(v0.security_level, SecurityLevel::default());
385        assert_eq!(v0.key_type, KeyType::default());
386        assert!(!v0.read_only);
387        assert_eq!(v0.data.as_slice().len(), 0);
388        assert!(v0.disabled_at.is_none());
389        assert!(v0.contract_bounds.is_none());
390    }
391
392    // -- Ordering is derived: Clone + Eq + PartialOrd --
393    #[test]
394    fn test_keys_are_orderable_and_cloneable() {
395        let a = IdentityPublicKey::V0(IdentityPublicKeyV0 {
396            id: 1,
397            ..Default::default()
398        });
399        let b = IdentityPublicKey::V0(IdentityPublicKeyV0 {
400            id: 2,
401            ..Default::default()
402        });
403        assert!(a < b);
404        let a_clone = a.clone();
405        assert_eq!(a, a_clone);
406    }
407}
408
409#[cfg(all(test, feature = "random-public-keys"))]
410mod random_tests {
411    use crate::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0;
412    use crate::identity::{IdentityPublicKey, KeyType, Purpose, SecurityLevel};
413    use platform_version::version::LATEST_PLATFORM_VERSION;
414
415    // -- random_key and random_keys with a seed are deterministic. --
416
417    #[test]
418    fn test_random_key_with_seed_is_deterministic() {
419        let k1 = IdentityPublicKey::random_key(1, Some(42), LATEST_PLATFORM_VERSION);
420        let k2 = IdentityPublicKey::random_key(1, Some(42), LATEST_PLATFORM_VERSION);
421        assert_eq!(k1, k2);
422    }
423
424    #[test]
425    fn test_random_keys_returns_requested_count() {
426        let keys = IdentityPublicKey::random_keys(0, 5, Some(7), LATEST_PLATFORM_VERSION);
427        assert_eq!(keys.len(), 5);
428        // Each should have a distinct id in [0, 5).
429        let mut ids: Vec<_> = keys.iter().map(|k| k.id()).collect();
430        ids.sort();
431        assert_eq!(ids, vec![0, 1, 2, 3, 4]);
432    }
433
434    #[test]
435    fn test_random_authentication_key_is_always_authentication_purpose() {
436        for id in 0..5u32 {
437            let k = IdentityPublicKey::random_authentication_key(
438                id,
439                Some(id as u64),
440                LATEST_PLATFORM_VERSION,
441            );
442            assert_eq!(k.purpose(), Purpose::AUTHENTICATION);
443        }
444    }
445
446    #[test]
447    fn test_random_authentication_keys_returns_requested_count_and_all_auth() {
448        let keys =
449            IdentityPublicKey::random_authentication_keys(0, 3, Some(11), LATEST_PLATFORM_VERSION);
450        assert_eq!(keys.len(), 3);
451        for k in &keys {
452            assert_eq!(k.purpose(), Purpose::AUTHENTICATION);
453        }
454    }
455
456    #[test]
457    fn test_random_ecdsa_master_authentication_key_has_expected_attributes() {
458        let (key, _priv) = IdentityPublicKey::random_ecdsa_master_authentication_key(
459            0,
460            Some(1),
461            LATEST_PLATFORM_VERSION,
462        )
463        .expect("expected master authentication key");
464        assert_eq!(key.key_type(), KeyType::ECDSA_SECP256K1);
465        assert_eq!(key.purpose(), Purpose::AUTHENTICATION);
466        assert_eq!(key.security_level(), SecurityLevel::MASTER);
467        assert!(!key.read_only());
468        assert_eq!(key.data().len(), 33);
469    }
470
471    #[test]
472    fn test_random_ecdsa_critical_level_authentication_key_attributes() {
473        let (key, _priv) = IdentityPublicKey::random_ecdsa_critical_level_authentication_key(
474            1,
475            Some(2),
476            LATEST_PLATFORM_VERSION,
477        )
478        .expect("expected critical auth key");
479        assert_eq!(key.key_type(), KeyType::ECDSA_SECP256K1);
480        assert_eq!(key.purpose(), Purpose::AUTHENTICATION);
481        assert_eq!(key.security_level(), SecurityLevel::CRITICAL);
482    }
483
484    #[test]
485    fn test_random_ecdsa_high_level_authentication_key_attributes() {
486        let (key, _priv) = IdentityPublicKey::random_ecdsa_high_level_authentication_key(
487            2,
488            Some(3),
489            LATEST_PLATFORM_VERSION,
490        )
491        .expect("expected high-level auth key");
492        assert_eq!(key.key_type(), KeyType::ECDSA_SECP256K1);
493        assert_eq!(key.purpose(), Purpose::AUTHENTICATION);
494        assert_eq!(key.security_level(), SecurityLevel::HIGH);
495    }
496
497    #[test]
498    fn test_random_masternode_owner_key_has_owner_purpose_and_is_read_only() {
499        let (key, _priv) =
500            IdentityPublicKey::random_masternode_owner_key(3, Some(4), LATEST_PLATFORM_VERSION)
501                .expect("expected masternode owner key");
502        assert_eq!(key.key_type(), KeyType::ECDSA_HASH160);
503        assert_eq!(key.purpose(), Purpose::OWNER);
504        assert_eq!(key.security_level(), SecurityLevel::CRITICAL);
505        assert!(key.read_only());
506    }
507
508    #[test]
509    fn test_random_masternode_transfer_key_has_transfer_purpose_and_is_read_only() {
510        let (key, _priv) =
511            IdentityPublicKey::random_masternode_transfer_key(4, Some(5), LATEST_PLATFORM_VERSION)
512                .expect("expected masternode transfer key");
513        assert_eq!(key.key_type(), KeyType::ECDSA_HASH160);
514        assert_eq!(key.purpose(), Purpose::TRANSFER);
515        assert_eq!(key.security_level(), SecurityLevel::CRITICAL);
516        assert!(key.read_only());
517    }
518
519    #[test]
520    fn test_main_keys_with_random_authentication_keys_errors_when_count_below_two() {
521        use rand::{rngs::StdRng, SeedableRng};
522        let mut rng = StdRng::seed_from_u64(1);
523        let err =
524            IdentityPublicKey::main_keys_with_random_authentication_keys_with_private_keys_with_rng(
525                1,
526                &mut rng,
527                LATEST_PLATFORM_VERSION,
528            )
529            .unwrap_err();
530        match err {
531            crate::ProtocolError::PublicKeyGenerationError(msg) => {
532                assert!(msg.contains("at least 2"));
533            }
534            other => panic!("expected PublicKeyGenerationError, got {:?}", other),
535        }
536    }
537
538    #[test]
539    fn test_main_keys_with_random_authentication_keys_count_two() {
540        use rand::{rngs::StdRng, SeedableRng};
541        let mut rng = StdRng::seed_from_u64(2);
542        let keys = IdentityPublicKey::main_keys_with_random_authentication_keys_with_private_keys_with_rng(
543            2,
544            &mut rng,
545            LATEST_PLATFORM_VERSION,
546        )
547        .expect("expected keys");
548        assert_eq!(keys.len(), 2);
549        // Position 0 must be Master ECDSA, position 1 must be High ECDSA.
550        assert_eq!(keys[0].0.security_level(), SecurityLevel::MASTER);
551        assert_eq!(keys[0].0.key_type(), KeyType::ECDSA_SECP256K1);
552        assert_eq!(keys[1].0.security_level(), SecurityLevel::HIGH);
553        assert_eq!(keys[1].0.key_type(), KeyType::ECDSA_SECP256K1);
554    }
555
556    #[test]
557    fn test_main_keys_with_random_authentication_keys_count_three() {
558        use rand::{rngs::StdRng, SeedableRng};
559        let mut rng = StdRng::seed_from_u64(3);
560        let keys = IdentityPublicKey::main_keys_with_random_authentication_keys_with_private_keys_with_rng(
561            3,
562            &mut rng,
563            LATEST_PLATFORM_VERSION,
564        )
565        .expect("expected keys");
566        assert_eq!(keys.len(), 3);
567        assert_eq!(keys[0].0.security_level(), SecurityLevel::MASTER);
568        assert_eq!(keys[1].0.security_level(), SecurityLevel::CRITICAL);
569        assert_eq!(keys[2].0.security_level(), SecurityLevel::HIGH);
570    }
571
572    #[test]
573    fn test_random_unique_keys_with_rng_matches_count() {
574        use rand::{rngs::StdRng, SeedableRng};
575        let mut rng = StdRng::seed_from_u64(9);
576        let keys =
577            IdentityPublicKey::random_unique_keys_with_rng(4, &mut rng, LATEST_PLATFORM_VERSION)
578                .expect("expected keys");
579        assert_eq!(keys.len(), 4);
580        // IDs should be 0..4.
581        let ids: Vec<_> = keys.iter().map(|k| k.id()).collect();
582        assert_eq!(ids, vec![0, 1, 2, 3]);
583    }
584
585    #[test]
586    fn test_random_keys_with_rng_returns_requested_count() {
587        use rand::{rngs::StdRng, SeedableRng};
588        let mut rng = StdRng::seed_from_u64(10);
589        let keys = IdentityPublicKey::random_keys_with_rng(3, &mut rng, LATEST_PLATFORM_VERSION);
590        assert_eq!(keys.len(), 3);
591    }
592
593    #[test]
594    fn test_random_authentication_keys_with_private_keys_with_rng_returns_pairs() {
595        use rand::{rngs::StdRng, SeedableRng};
596        let mut rng = StdRng::seed_from_u64(11);
597        let pairs = IdentityPublicKey::random_authentication_keys_with_private_keys_with_rng(
598            5,
599            3,
600            &mut rng,
601            LATEST_PLATFORM_VERSION,
602        )
603        .expect("expected pairs");
604        assert_eq!(pairs.len(), 3);
605        let ids: Vec<_> = pairs.iter().map(|(k, _)| k.id()).collect();
606        assert_eq!(ids, vec![5, 6, 7]);
607        for (k, _) in &pairs {
608            assert_eq!(k.purpose(), Purpose::AUTHENTICATION);
609        }
610    }
611
612    #[test]
613    fn test_random_voting_key_with_rng_attributes() {
614        use rand::{rngs::StdRng, SeedableRng};
615        let mut rng = StdRng::seed_from_u64(13);
616        let (k, _priv) =
617            IdentityPublicKey::random_voting_key_with_rng(0, &mut rng, LATEST_PLATFORM_VERSION)
618                .expect("expected voting key");
619        assert_eq!(k.purpose(), Purpose::VOTING);
620        assert_eq!(k.key_type(), KeyType::ECDSA_HASH160);
621        assert_eq!(k.security_level(), SecurityLevel::MEDIUM);
622    }
623
624    #[test]
625    fn test_random_key_with_known_attributes_honors_inputs() {
626        use crate::identity::contract_bounds::ContractBounds;
627        use platform_value::Identifier;
628        use rand::{rngs::StdRng, SeedableRng};
629        let mut rng = StdRng::seed_from_u64(17);
630        let bounds = ContractBounds::SingleContract {
631            id: Identifier::from([0x77u8; 32]),
632        };
633        let (k, _priv) = IdentityPublicKey::random_key_with_known_attributes(
634            42,
635            &mut rng,
636            Purpose::TRANSFER,
637            SecurityLevel::CRITICAL,
638            KeyType::ECDSA_SECP256K1,
639            Some(bounds.clone()),
640            LATEST_PLATFORM_VERSION,
641        )
642        .expect("expected key");
643        assert_eq!(k.id(), 42);
644        assert_eq!(k.purpose(), Purpose::TRANSFER);
645        assert_eq!(k.security_level(), SecurityLevel::CRITICAL);
646        assert_eq!(k.key_type(), KeyType::ECDSA_SECP256K1);
647        assert_eq!(k.contract_bounds(), Some(&bounds));
648    }
649}