Skip to main content

dpp/document/v0/serialize/
mod.rs

1use crate::data_contract::document_type::DocumentTypeRef;
2use crate::data_contract::errors::DataContractError;
3
4#[cfg(feature = "validation")]
5use crate::prelude::ConsensusValidationResult;
6
7use crate::prelude::DataContract;
8
9use crate::ProtocolError;
10
11use crate::document::serialization_traits::deserialize::v0::DocumentPlatformDeserializationMethodsV0;
12use crate::document::serialization_traits::serialize::v0::DocumentPlatformSerializationMethodsV0;
13use crate::document::serialization_traits::DocumentPlatformConversionMethodsV0;
14use crate::document::v0::DocumentV0;
15use crate::version::PlatformVersion;
16use integer_encoding::VarIntReader;
17
18use platform_version::version::FeatureVersion;
19
20use crate::consensus::basic::decode::DecodingError;
21#[cfg(feature = "validation")]
22use crate::consensus::basic::BasicError;
23#[cfg(feature = "validation")]
24use crate::consensus::ConsensusError;
25use crate::data_contract::accessors::v0::DataContractV0Getters;
26use crate::data_contract::config::DataContractConfig;
27
28mod v0;
29mod v1;
30mod v2;
31mod v3;
32
33// Each serialization format generation lives in its own file (v0.rs–v3.rs);
34// the trait impls below are one-line dispatch shims into the inherent
35// methods those files define. Consensus discipline: a shipped format's file
36// must never change, and a diff touching one is immediately suspect.
37impl DocumentPlatformSerializationMethodsV0 for DocumentV0 {
38    /// Format 0 — implementation in [`v0`].
39    fn serialize_v0(&self, document_type: DocumentTypeRef) -> Result<Vec<u8>, ProtocolError> {
40        DocumentV0::serialize_v0(self, document_type)
41    }
42
43    /// Format 1 — implementation in [`v1`].
44    fn serialize_v1(&self, document_type: DocumentTypeRef) -> Result<Vec<u8>, ProtocolError> {
45        DocumentV0::serialize_v1(self, document_type)
46    }
47
48    /// Format 2 — implementation in [`v2`].
49    fn serialize_v2(&self, document_type: DocumentTypeRef) -> Result<Vec<u8>, ProtocolError> {
50        DocumentV0::serialize_v2(self, document_type)
51    }
52
53    /// Format 3 — implementation in [`v3`].
54    fn serialize_v3(&self, document_type: DocumentTypeRef) -> Result<Vec<u8>, ProtocolError> {
55        DocumentV0::serialize_v3(self, document_type)
56    }
57}
58
59impl DocumentPlatformDeserializationMethodsV0 for DocumentV0 {
60    /// Format 0 — implementation in [`v0`].
61    fn from_bytes_v0(
62        serialized_document: &[u8],
63        document_type: DocumentTypeRef,
64        platform_version: &PlatformVersion,
65    ) -> Result<Self, DataContractError> {
66        DocumentV0::from_bytes_v0(serialized_document, document_type, platform_version)
67    }
68
69    /// Format 1 — implementation in [`v1`].
70    fn from_bytes_v1(
71        serialized_document: &[u8],
72        document_type: DocumentTypeRef,
73        platform_version: &PlatformVersion,
74    ) -> Result<Self, DataContractError> {
75        DocumentV0::from_bytes_v1(serialized_document, document_type, platform_version)
76    }
77
78    /// Format 2 — implementation in [`v2`].
79    fn from_bytes_v2(
80        serialized_document: &[u8],
81        document_type: DocumentTypeRef,
82        platform_version: &PlatformVersion,
83    ) -> Result<Self, DataContractError> {
84        DocumentV0::from_bytes_v2(serialized_document, document_type, platform_version)
85    }
86
87    /// Format 3 — implementation in [`v3`].
88    fn from_bytes_v3(
89        serialized_document: &[u8],
90        document_type: DocumentTypeRef,
91        platform_version: &PlatformVersion,
92    ) -> Result<Self, DataContractError> {
93        DocumentV0::from_bytes_v3(serialized_document, document_type, platform_version)
94    }
95}
96
97impl DocumentPlatformConversionMethodsV0 for DocumentV0 {
98    /// Serializes the document.
99    ///
100    /// The serialization of a document follows the pattern:
101    /// id 32 bytes + owner_id 32 bytes + encoded values byte arrays
102    fn serialize(
103        &self,
104        document_type: DocumentTypeRef,
105        contract: &DataContract,
106        platform_version: &PlatformVersion,
107    ) -> Result<Vec<u8>, ProtocolError> {
108        if matches!(contract, DataContract::V0(_))
109            || matches!(contract.config(), DataContractConfig::V0(_))
110        {
111            // Any data contract in version 0 should always serialize documents in version 0
112            // This is because integers in such a data contract if made through normal versioning should always
113            // be i64
114            // While it's possible in theory maybe that they are not i64 using serialize_v0
115            // will encode all integers as i64.
116            self.serialize_v0(document_type)
117        } else {
118            match platform_version
119                .dpp
120                .document_versions
121                .document_serialization_version
122                .default_current_version
123            {
124                // Version 0 is the original format, the default for protocol
125                // versions 1 through 8. Every integer is encoded as an i64
126                // regardless of its schema type.
127                0 => self.serialize_v0(document_type),
128                // Version 1 coincides with protocol version 9, which contains tokens, new document types,
129                // and most importantly different integer types.
130                // Document types now have properties that are known to be things like u8, i32 etc.
131                1 => self.serialize_v1(document_type),
132                // Version 2 coincides with protocol version 10: it adds the
133                // $creatorId field for document types that support transfers
134                // or trading, so the original creator survives ownership
135                // changes.
136                2 => self.serialize_v2(document_type),
137                // Version 3 coincides with protocol version 14: it stamps the
138                // document with the contract version its bytes conform to,
139                // enabling `requiredSince` properties.
140                3 => self.serialize_v3(document_type),
141                version => Err(ProtocolError::UnknownVersionMismatch {
142                    method: "DocumentV0::serialize".to_string(),
143                    known_versions: vec![0, 1, 2, 3],
144                    received: version,
145                }),
146            }
147        }
148    }
149
150    fn serialize_specific_version(
151        &self,
152        document_type: DocumentTypeRef,
153        contract: &DataContract,
154        feature_version: FeatureVersion,
155    ) -> Result<Vec<u8>, ProtocolError> {
156        if (matches!(contract, DataContract::V0(_))
157            || matches!(contract.config(), DataContractConfig::V0(_)))
158            && feature_version != 0
159        {
160            // Any data contract in version 0 should always serialize documents in version 0
161            // This is because integers in such a data contract if made through normal versioning should always
162            // be i64
163            // While it's possible in theory maybe that they are not i64 using serialize_v0
164            // will encode all integers as i64.
165            return Err(ProtocolError::NotSupported("Serializing with data contract version 0 or data contract config version 0 is not supported outside of feature version 0".to_string()));
166        };
167        match feature_version {
168            0 => self.serialize_v0(document_type),
169            1 => self.serialize_v1(document_type),
170            2 => self.serialize_v2(document_type),
171            3 => self.serialize_v3(document_type),
172            version => Err(ProtocolError::UnknownVersionMismatch {
173                method: "DocumentV0::serialize".to_string(),
174                known_versions: vec![0, 1, 2, 3],
175                received: version,
176            }),
177        }
178    }
179
180    /// Reads a serialized document and creates a DocumentV0 from it.
181    fn from_bytes(
182        mut serialized_document: &[u8],
183        document_type: DocumentTypeRef,
184        platform_version: &PlatformVersion,
185    ) -> Result<Self, ProtocolError> {
186        let serialized_version = serialized_document.read_varint().map_err(|_| {
187            DataContractError::DecodingDocumentError(DecodingError::new(
188                "error reading revision from serialized document for revision".to_string(),
189            ))
190        })?;
191        match serialized_version {
192            0 => {
193                match DocumentV0::from_bytes_v0(
194                    serialized_document,
195                    document_type,
196                    platform_version,
197                )
198                .map_err(ProtocolError::DataContractError)
199                {
200                    Ok(document) => Ok(document),
201                    Err(first_err) => {
202                        // let's try decoding in V1 just to be safe
203                        // Version 0 will decode all integers as I64
204                        // Version 1 will decode all integers properly
205                        // When version was 0 used (protocol version 1 to 8) integers other than I64
206                        // existed, but were probably never used, which is why we try v1 just to be safe
207                        match DocumentV0::from_bytes_v1(
208                            serialized_document,
209                            document_type,
210                            platform_version,
211                        ) {
212                            Ok(document_from_version_1_deserialization) => {
213                                Ok(document_from_version_1_deserialization)
214                            }
215                            Err(_) => Err(first_err),
216                        }
217                    }
218                }
219            }
220            1 => DocumentV0::from_bytes_v1(serialized_document, document_type, platform_version)
221                .map_err(ProtocolError::DataContractError),
222            2 => DocumentV0::from_bytes_v2(serialized_document, document_type, platform_version)
223                .map_err(ProtocolError::DataContractError),
224            3 => DocumentV0::from_bytes_v3(serialized_document, document_type, platform_version)
225                .map_err(ProtocolError::DataContractError),
226            version => Err(ProtocolError::UnknownVersionMismatch {
227                method: "Document::from_bytes (deserialization)".to_string(),
228                known_versions: vec![0, 1, 2, 3],
229                received: version,
230            }),
231        }
232    }
233
234    /// Reads a serialized document and creates a DocumentV0 from it.
235    #[cfg(feature = "validation")]
236    fn from_bytes_in_consensus(
237        mut serialized_document: &[u8],
238        document_type: DocumentTypeRef,
239        platform_version: &PlatformVersion,
240    ) -> Result<ConsensusValidationResult<Self>, ProtocolError> {
241        let serialized_version = serialized_document.read_varint().map_err(|_| {
242            DataContractError::DecodingDocumentError(DecodingError::new(
243                "error reading revision from serialized document for revision".to_string(),
244            ))
245        })?;
246        match serialized_version {
247            0 => {
248                match DocumentV0::from_bytes_v0(
249                    serialized_document,
250                    document_type,
251                    platform_version,
252                ) {
253                    Ok(document) => Ok(ConsensusValidationResult::new_with_data(document)),
254                    Err(first_err) => {
255                        // let's try decoding in V1 just to be safe
256                        // Version 0 will decode all integers as I64
257                        // Version 1 will decode all integers properly
258                        // When version was 0 used (protocol version 1 to 8) integers other than I64
259                        // existed, but were probably never used, which is why we try v1 just to be safe
260                        match DocumentV0::from_bytes_v1(
261                            serialized_document,
262                            document_type,
263                            platform_version,
264                        ) {
265                            Ok(document_from_version_1_deserialization) => {
266                                Ok(ConsensusValidationResult::new_with_data(
267                                    document_from_version_1_deserialization,
268                                ))
269                            }
270                            Err(_) => Ok(ConsensusValidationResult::new_with_error(
271                                ConsensusError::BasicError(BasicError::ContractError(first_err)),
272                            )),
273                        }
274                    }
275                }
276            }
277            1 => {
278                match DocumentV0::from_bytes_v1(
279                    serialized_document,
280                    document_type,
281                    platform_version,
282                ) {
283                    Ok(document) => Ok(ConsensusValidationResult::new_with_data(document)),
284                    Err(err) => Ok(ConsensusValidationResult::new_with_error(
285                        ConsensusError::BasicError(BasicError::ContractError(err)),
286                    )),
287                }
288            }
289            2 => {
290                match DocumentV0::from_bytes_v2(
291                    serialized_document,
292                    document_type,
293                    platform_version,
294                ) {
295                    Ok(document) => Ok(ConsensusValidationResult::new_with_data(document)),
296                    Err(err) => Ok(ConsensusValidationResult::new_with_error(
297                        ConsensusError::BasicError(BasicError::ContractError(err)),
298                    )),
299                }
300            }
301            3 => {
302                match DocumentV0::from_bytes_v3(
303                    serialized_document,
304                    document_type,
305                    platform_version,
306                ) {
307                    Ok(document) => Ok(ConsensusValidationResult::new_with_data(document)),
308                    Err(err) => Ok(ConsensusValidationResult::new_with_error(
309                        ConsensusError::BasicError(BasicError::ContractError(err)),
310                    )),
311                }
312            }
313            version => Err(ProtocolError::UnknownVersionMismatch {
314                method: "Document::from_bytes (deserialization)".to_string(),
315                known_versions: vec![0, 1, 2, 3],
316                received: version,
317            }),
318        }
319    }
320}
321
322#[cfg(test)]
323mod tests;