Skip to main content

dpp/identity/identity_public_key/v0/conversion/
json.rs

1use crate::identity::identity_public_key::conversion::json::IdentityPublicKeyJsonConversionMethodsV0;
2use crate::identity::identity_public_key::conversion::platform_value::IdentityPublicKeyPlatformValueConversionMethodsV0;
3use crate::identity::identity_public_key::fields::BINARY_DATA_FIELDS;
4use crate::identity::identity_public_key::v0::IdentityPublicKeyV0;
5use crate::version::PlatformVersion;
6use crate::ProtocolError;
7use platform_value::{ReplacementType, Value};
8use serde_json::Value as JsonValue;
9use std::convert::{TryFrom, TryInto};
10
11impl IdentityPublicKeyJsonConversionMethodsV0 for IdentityPublicKeyV0 {
12    fn to_json_object(&self) -> Result<JsonValue, ProtocolError> {
13        self.to_cleaned_object()?
14            .try_into_validating_json()
15            .map_err(ProtocolError::ValueError)
16    }
17
18    fn to_json(&self) -> Result<JsonValue, ProtocolError> {
19        self.to_cleaned_object()?
20            .try_into()
21            .map_err(ProtocolError::ValueError)
22    }
23
24    fn from_json_object(
25        raw_object: JsonValue,
26        platform_version: &PlatformVersion,
27    ) -> Result<Self, ProtocolError> {
28        let mut value: Value = raw_object.into();
29        value.replace_at_paths(BINARY_DATA_FIELDS, ReplacementType::BinaryBytes)?;
30        Self::from_object(value, platform_version)
31    }
32}
33
34impl TryFrom<&str> for IdentityPublicKeyV0 {
35    type Error = ProtocolError;
36
37    fn try_from(value: &str) -> Result<Self, Self::Error> {
38        let mut platform_value: Value = serde_json::from_str::<JsonValue>(value)
39            .map_err(|e| ProtocolError::StringDecodeError(e.to_string()))?
40            .into();
41        platform_value.replace_at_paths(BINARY_DATA_FIELDS, ReplacementType::BinaryBytes)?;
42        platform_value.try_into().map_err(ProtocolError::ValueError)
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::identity::{KeyType, Purpose, SecurityLevel};
50    use platform_value::BinaryData;
51    use platform_version::version::LATEST_PLATFORM_VERSION;
52
53    fn sample_v0(disabled_at: Option<u64>) -> IdentityPublicKeyV0 {
54        IdentityPublicKeyV0 {
55            id: 1,
56            purpose: Purpose::AUTHENTICATION,
57            security_level: SecurityLevel::MASTER,
58            contract_bounds: None,
59            key_type: KeyType::ECDSA_SECP256K1,
60            read_only: false,
61            data: BinaryData::new(vec![0x01; 33]),
62            disabled_at,
63        }
64    }
65
66    #[test]
67    fn to_json_returns_object() {
68        let key = sample_v0(None);
69        let json = key.to_json().expect("to_json succeeds");
70        let obj = json.as_object().expect("expected json object");
71        assert!(obj.contains_key("id"));
72        assert!(obj.contains_key("type"));
73        assert!(obj.contains_key("purpose"));
74        assert!(obj.contains_key("securityLevel"));
75        assert!(obj.contains_key("readOnly"));
76        assert!(obj.contains_key("data"));
77        // disabledAt is absent because it was None (to_cleaned_object removes it).
78        assert!(!obj.contains_key("disabledAt"));
79    }
80
81    #[test]
82    fn to_json_includes_disabled_at_when_some() {
83        let key = sample_v0(Some(1_000_000));
84        let json = key.to_json().expect("to_json succeeds");
85        let obj = json.as_object().expect("object");
86        assert!(obj.contains_key("disabledAt"));
87    }
88
89    #[test]
90    fn to_json_object_data_is_byte_array() {
91        // to_json_object goes through try_into_validating_json, which renders bytes as arrays.
92        let key = sample_v0(None);
93        let json = key.to_json_object().expect("to_json_object succeeds");
94        let obj = json.as_object().expect("object");
95        let data = obj.get("data").expect("data field").as_array().expect(
96            "to_json_object should encode binary data as a JSON array of bytes (validating form)",
97        );
98        assert_eq!(data.len(), 33);
99    }
100
101    #[test]
102    fn from_json_object_roundtrip() {
103        let key = sample_v0(None);
104        let json = key.to_json().unwrap();
105        let back = IdentityPublicKeyV0::from_json_object(json, LATEST_PLATFORM_VERSION).unwrap();
106        assert_eq!(back, key);
107    }
108
109    #[test]
110    fn try_from_str_parses_canonical_json() {
111        // Data is base64 of 33 0xAB bytes.
112        let bytes = [0xABu8; 33];
113        let b64 = platform_value::string_encoding::encode(
114            &bytes,
115            platform_value::string_encoding::Encoding::Base64,
116        );
117        let s = format!(
118            r#"{{
119                "id": 7,
120                "type": 0,
121                "purpose": 0,
122                "securityLevel": 0,
123                "readOnly": false,
124                "data": "{}"
125            }}"#,
126            b64
127        );
128        let key: IdentityPublicKeyV0 = s.as_str().try_into().expect("parse succeeds");
129        assert_eq!(key.id, 7);
130        assert_eq!(key.data.as_slice(), &vec![0xABu8; 33][..]);
131    }
132
133    #[test]
134    fn try_from_str_fails_on_invalid_json() {
135        let result = IdentityPublicKeyV0::try_from("not valid json");
136        match result {
137            Err(ProtocolError::StringDecodeError(_)) => {}
138            other => panic!("expected StringDecodeError, got {:?}", other),
139        }
140    }
141
142    #[test]
143    fn try_from_str_fails_when_data_is_not_base64() {
144        let s = r#"{
145            "id": 1,
146            "type": 0,
147            "purpose": 0,
148            "securityLevel": 0,
149            "readOnly": false,
150            "data": "!!!not-base64!!!"
151        }"#;
152        let result = IdentityPublicKeyV0::try_from(s);
153        assert!(result.is_err());
154    }
155
156    #[test]
157    fn from_json_object_fails_on_missing_fields() {
158        let json = serde_json::json!({ "id": 1 });
159        let result = IdentityPublicKeyV0::from_json_object(json, LATEST_PLATFORM_VERSION);
160        assert!(result.is_err());
161    }
162}