Skip to main content

dpp/data_contract/v1/serialization/
mod.rs

1use crate::data_contract::document_type::DocumentType;
2use crate::data_contract::serialized_version::v0::DataContractInSerializationFormatV0;
3use crate::data_contract::serialized_version::DataContractInSerializationFormat;
4use crate::data_contract::{DataContract, DataContractV1};
5use crate::version::{PlatformVersion, PlatformVersionCurrentVersion};
6use crate::ProtocolError;
7use std::collections::BTreeMap;
8
9use crate::data_contract::serialized_version::v1::DataContractInSerializationFormatV1;
10use crate::validation::operations::ProtocolValidationOperation;
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12
13impl Serialize for DataContractV1 {
14    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
15    where
16        S: Serializer,
17    {
18        let data_contract: DataContract = self.clone().into();
19        let serialization_format = DataContractInSerializationFormatV1::from(data_contract);
20        serialization_format.serialize(serializer)
21    }
22}
23
24impl<'de> Deserialize<'de> for DataContractV1 {
25    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
26    where
27        D: Deserializer<'de>,
28    {
29        let serialization_format = DataContractInSerializationFormatV1::deserialize(deserializer)?;
30        let current_version = PlatformVersion::get_version_or_current_or_latest(None)
31            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
32        // when deserializing from json/platform_value/cbor we always want to validate (as this is not coming from the state)
33        DataContractV1::try_from_platform_versioned_v1(
34            serialization_format,
35            true,
36            &mut vec![],
37            current_version,
38        )
39        .map_err(serde::de::Error::custom)
40    }
41}
42
43impl DataContractV1 {
44    pub(in crate::data_contract) fn try_from_platform_versioned(
45        value: DataContractInSerializationFormat,
46        full_validation: bool,
47        validation_operations: &mut Vec<ProtocolValidationOperation>,
48        platform_version: &PlatformVersion,
49    ) -> Result<Self, ProtocolError> {
50        match value {
51            DataContractInSerializationFormat::V0(serialization_format_v0) => {
52                let data_contract = DataContractV1::try_from_platform_versioned_v0(
53                    serialization_format_v0,
54                    full_validation,
55                    validation_operations,
56                    platform_version,
57                )?;
58
59                Ok(data_contract)
60            }
61            DataContractInSerializationFormat::V1(serialization_format_v1) => {
62                let data_contract = DataContractV1::try_from_platform_versioned_v1(
63                    serialization_format_v1,
64                    full_validation,
65                    validation_operations,
66                    platform_version,
67                )?;
68
69                Ok(data_contract)
70            }
71        }
72    }
73
74    pub(in crate::data_contract) fn try_from_platform_versioned_v0(
75        data_contract_data: DataContractInSerializationFormatV0,
76        full_validation: bool,
77        validation_operations: &mut Vec<ProtocolValidationOperation>,
78        platform_version: &PlatformVersion,
79    ) -> Result<Self, ProtocolError> {
80        let DataContractInSerializationFormatV0 {
81            id,
82            config,
83            version,
84            owner_id,
85            document_schemas,
86            schema_defs,
87        } = data_contract_data;
88
89        let document_types = DocumentType::create_document_types_from_document_schemas(
90            id,
91            1,
92            data_contract_data.config.version(),
93            document_schemas,
94            schema_defs.as_ref(),
95            &BTreeMap::new(),
96            &config,
97            full_validation,
98            false,
99            validation_operations,
100            platform_version,
101        )?;
102
103        let data_contract = DataContractV1 {
104            id,
105            version,
106            owner_id,
107            document_types,
108            config,
109            schema_defs,
110            created_at: None,
111            updated_at: None,
112            created_at_block_height: None,
113            updated_at_block_height: None,
114            created_at_epoch: None,
115            updated_at_epoch: None,
116            groups: Default::default(),
117            tokens: Default::default(),
118            keywords: Default::default(),
119            description: None,
120        };
121
122        Ok(data_contract)
123    }
124
125    pub(in crate::data_contract) fn try_from_platform_versioned_v1(
126        data_contract_data: DataContractInSerializationFormatV1,
127        full_validation: bool,
128        validation_operations: &mut Vec<ProtocolValidationOperation>,
129        platform_version: &PlatformVersion,
130    ) -> Result<Self, ProtocolError> {
131        let DataContractInSerializationFormatV1 {
132            id,
133            config,
134            version,
135            owner_id,
136            document_schemas,
137            schema_defs,
138            created_at,
139            updated_at,
140            created_at_block_height,
141            updated_at_block_height,
142            created_at_epoch,
143            updated_at_epoch,
144            groups,
145            tokens,
146            keywords,
147            description,
148        } = data_contract_data;
149
150        let document_types = DocumentType::create_document_types_from_document_schemas(
151            id,
152            1,
153            data_contract_data.config.version(),
154            document_schemas,
155            schema_defs.as_ref(),
156            &tokens,
157            &config,
158            full_validation,
159            !tokens.is_empty(),
160            validation_operations,
161            platform_version,
162        )?;
163
164        let data_contract = DataContractV1 {
165            id,
166            version,
167            owner_id,
168            document_types,
169            config,
170            schema_defs,
171            created_at,
172            updated_at,
173            created_at_block_height,
174            updated_at_block_height,
175            created_at_epoch,
176            updated_at_epoch,
177            groups,
178            tokens,
179            keywords: keywords
180                .into_iter()
181                .map(|keyword| keyword.to_lowercase())
182                .collect(),
183            description,
184        };
185
186        Ok(data_contract)
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use crate::data_contract::DataContract;
193    use crate::identity::accessors::IdentityGettersV0;
194    use crate::identity::Identity;
195    use crate::serialization::{
196        PlatformDeserializableWithPotentialValidationFromVersionedStructure,
197        PlatformSerializableWithPlatformVersion,
198    };
199    use crate::tests::fixtures::get_data_contract_fixture;
200    use crate::version::PlatformVersion;
201    use platform_version::version::LATEST_PLATFORM_VERSION;
202
203    #[test]
204    #[cfg(feature = "random-identities")]
205    fn data_contract_ser_de() {
206        // V1 of the contract is first present in protocol version 7
207        let platform_version = PlatformVersion::get(7).expect("expected protocol version 7");
208        let identity = Identity::random_identity(5, Some(5), platform_version)
209            .expect("expected a random identity");
210        let contract =
211            get_data_contract_fixture(Some(identity.id()), 0, platform_version.protocol_version)
212                .data_contract_owned();
213        let bytes = contract
214            .serialize_to_bytes_with_platform_version(LATEST_PLATFORM_VERSION)
215            .expect("expected to serialize");
216        let recovered_contract =
217            DataContract::versioned_deserialize(&bytes, false, platform_version)
218                .expect("expected to deserialize state transition");
219        assert_eq!(contract, recovered_contract);
220    }
221
222    #[test]
223    #[cfg(feature = "random-identities")]
224    fn data_contract_v1_serde_json_roundtrip() {
225        use crate::data_contract::accessors::v0::DataContractV0Getters;
226        use crate::data_contract::DataContractV1;
227
228        // V1 contracts are first produced at platform version 9
229        let platform_version = PlatformVersion::get(9).expect("expected protocol version 9");
230        let identity = Identity::random_identity(5, Some(5), platform_version)
231            .expect("expected a random identity");
232        let contract =
233            get_data_contract_fixture(Some(identity.id()), 0, platform_version.protocol_version)
234                .data_contract_owned();
235        let v1 = contract.into_v1().expect("expected V1 contract");
236
237        let json = serde_json::to_string(&v1).expect("expected to serialize to JSON");
238        let recovered: DataContractV1 =
239            serde_json::from_str(&json).expect("expected to deserialize from JSON");
240
241        // Schema normalization during deserialization means full equality may differ;
242        // verify stable identity fields to confirm a successful roundtrip.
243        assert_eq!(v1.id(), recovered.id());
244        assert_eq!(v1.owner_id(), recovered.owner_id());
245        assert_eq!(v1.version(), recovered.version());
246    }
247}