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