dpp/document/v0/
serialize.rs

1use crate::data_contract::document_type::{DocumentPropertyType, DocumentTypeRef};
2use crate::data_contract::errors::DataContractError;
3
4use crate::document::property_names::{
5    CREATED_AT, CREATED_AT_BLOCK_HEIGHT, CREATED_AT_CORE_BLOCK_HEIGHT, PRICE, TRANSFERRED_AT,
6    TRANSFERRED_AT_BLOCK_HEIGHT, TRANSFERRED_AT_CORE_BLOCK_HEIGHT, UPDATED_AT,
7    UPDATED_AT_BLOCK_HEIGHT, UPDATED_AT_CORE_BLOCK_HEIGHT,
8};
9
10#[cfg(feature = "validation")]
11use crate::prelude::ConsensusValidationResult;
12
13use crate::prelude::{DataContract, Revision};
14
15use crate::ProtocolError;
16
17use crate::data_contract::document_type::accessors::DocumentTypeV0Getters;
18use crate::data_contract::document_type::methods::DocumentTypeBasicMethods;
19use crate::document::serialization_traits::deserialize::v0::DocumentPlatformDeserializationMethodsV0;
20use crate::document::serialization_traits::serialize::v0::DocumentPlatformSerializationMethodsV0;
21use crate::document::serialization_traits::DocumentPlatformConversionMethodsV0;
22use crate::document::v0::DocumentV0;
23use crate::version::PlatformVersion;
24use byteorder::{BigEndian, ReadBytesExt};
25use integer_encoding::{VarInt, VarIntReader};
26
27use platform_value::{Identifier, Value};
28use platform_version::version::FeatureVersion;
29
30use std::collections::BTreeMap;
31
32use crate::consensus::basic::decode::DecodingError;
33#[cfg(feature = "validation")]
34use crate::consensus::basic::BasicError;
35#[cfg(feature = "validation")]
36use crate::consensus::ConsensusError;
37use crate::data_contract::accessors::v0::DataContractV0Getters;
38use crate::data_contract::config::DataContractConfig;
39use crate::nft::TradeMode;
40use std::io::{BufReader, Read};
41
42impl DocumentPlatformSerializationMethodsV0 for DocumentV0 {
43    /// Serializes the document.
44    ///
45    /// The serialization of a document follows the pattern:
46    /// id 32 bytes + owner_id 32 bytes + encoded values byte arrays
47    /// In serialize v0 all integers are always encoded as i64s
48    fn serialize_v0(&self, document_type: DocumentTypeRef) -> Result<Vec<u8>, ProtocolError> {
49        let mut buffer: Vec<u8> = 0u64.encode_var_vec(); //version 0
50
51        // $id
52        buffer.extend(self.id.as_slice());
53
54        // $ownerId
55        buffer.extend(self.owner_id.as_slice());
56
57        // $revision
58        if let Some(revision) = self.revision {
59            buffer.extend(revision.encode_var_vec())
60        } else if document_type.requires_revision() {
61            buffer.extend((1 as Revision).encode_var_vec())
62        }
63
64        let mut bitwise_exists_flag: u16 = 0;
65
66        let mut time_fields_data_buffer = vec![];
67
68        // $createdAt
69        if let Some(created_at) = &self.created_at {
70            bitwise_exists_flag |= 1;
71            // dbg!("we pushed created at {}", hex::encode(created_at.to_be_bytes()));
72            time_fields_data_buffer.extend(created_at.to_be_bytes());
73        } else if document_type.required_fields().contains(CREATED_AT) {
74            return Err(ProtocolError::DataContractError(
75                DataContractError::MissingRequiredKey(
76                    "created at field is not present".to_string(),
77                ),
78            ));
79        }
80
81        // $updatedAt
82        if let Some(updated_at) = &self.updated_at {
83            bitwise_exists_flag |= 2;
84            // dbg!("we pushed updated at {}", hex::encode(updated_at.to_be_bytes()));
85            time_fields_data_buffer.extend(updated_at.to_be_bytes());
86        } else if document_type.required_fields().contains(UPDATED_AT) {
87            return Err(ProtocolError::DataContractError(
88                DataContractError::MissingRequiredKey(
89                    "updated at field is not present".to_string(),
90                ),
91            ));
92        }
93
94        // $transferredAt
95        if let Some(transferred_at) = &self.transferred_at {
96            bitwise_exists_flag |= 4;
97            // dbg!("we pushed transferred at {}", hex::encode(transferred_at.to_be_bytes()));
98            time_fields_data_buffer.extend(transferred_at.to_be_bytes());
99        } else if document_type.required_fields().contains(TRANSFERRED_AT) {
100            return Err(ProtocolError::DataContractError(
101                DataContractError::MissingRequiredKey(
102                    "transferred at field is not present".to_string(),
103                ),
104            ));
105        }
106
107        // $createdAtBlockHeight
108        if let Some(created_at_block_height) = &self.created_at_block_height {
109            bitwise_exists_flag |= 8;
110            time_fields_data_buffer.extend(created_at_block_height.to_be_bytes());
111        } else if document_type
112            .required_fields()
113            .contains(CREATED_AT_BLOCK_HEIGHT)
114        {
115            return Err(ProtocolError::DataContractError(
116                DataContractError::MissingRequiredKey(
117                    "created_at_block_height field is not present".to_string(),
118                ),
119            ));
120        }
121
122        // $updatedAtBlockHeight
123        if let Some(updated_at_block_height) = &self.updated_at_block_height {
124            bitwise_exists_flag |= 16;
125            time_fields_data_buffer.extend(updated_at_block_height.to_be_bytes());
126        } else if document_type
127            .required_fields()
128            .contains(UPDATED_AT_BLOCK_HEIGHT)
129        {
130            return Err(ProtocolError::DataContractError(
131                DataContractError::MissingRequiredKey(
132                    "updated_at_block_height field is not present".to_string(),
133                ),
134            ));
135        }
136
137        // $transferredAtBlockHeight
138        if let Some(transferred_at_block_height) = &self.transferred_at_block_height {
139            bitwise_exists_flag |= 32;
140            time_fields_data_buffer.extend(transferred_at_block_height.to_be_bytes());
141        } else if document_type
142            .required_fields()
143            .contains(TRANSFERRED_AT_BLOCK_HEIGHT)
144        {
145            return Err(ProtocolError::DataContractError(
146                DataContractError::MissingRequiredKey(
147                    "transferred_at_block_height field is not present".to_string(),
148                ),
149            ));
150        }
151
152        // $createdAtCoreBlockHeight
153        if let Some(created_at_core_block_height) = &self.created_at_core_block_height {
154            bitwise_exists_flag |= 64;
155            time_fields_data_buffer.extend(created_at_core_block_height.to_be_bytes());
156        } else if document_type
157            .required_fields()
158            .contains(CREATED_AT_CORE_BLOCK_HEIGHT)
159        {
160            return Err(ProtocolError::DataContractError(
161                DataContractError::MissingRequiredKey(
162                    "created_at_core_block_height field is not present".to_string(),
163                ),
164            ));
165        }
166
167        // $updatedAtCoreBlockHeight
168        if let Some(updated_at_core_block_height) = &self.updated_at_core_block_height {
169            bitwise_exists_flag |= 128;
170            time_fields_data_buffer.extend(updated_at_core_block_height.to_be_bytes());
171        } else if document_type
172            .required_fields()
173            .contains(UPDATED_AT_CORE_BLOCK_HEIGHT)
174        {
175            return Err(ProtocolError::DataContractError(
176                DataContractError::MissingRequiredKey(
177                    "updated_at_core_block_height field is not present".to_string(),
178                ),
179            ));
180        }
181
182        // $transferredAtCoreBlockHeight
183        if let Some(transferred_at_core_block_height) = &self.transferred_at_core_block_height {
184            bitwise_exists_flag |= 256;
185            time_fields_data_buffer.extend(transferred_at_core_block_height.to_be_bytes());
186        } else if document_type
187            .required_fields()
188            .contains(TRANSFERRED_AT_CORE_BLOCK_HEIGHT)
189        {
190            return Err(ProtocolError::DataContractError(
191                DataContractError::MissingRequiredKey(
192                    "transferred_at_core_block_height field is not present".to_string(),
193                ),
194            ));
195        }
196
197        buffer.extend(bitwise_exists_flag.to_be_bytes().as_slice());
198        buffer.append(&mut time_fields_data_buffer);
199
200        // Now we serialize the price which might not be necessary unless called for by the document type
201
202        if document_type.trade_mode().seller_sets_price() {
203            if let Some(price) = self.properties.get(PRICE) {
204                buffer.push(1);
205                let price_as_u64: u64 = price.to_integer().map_err(ProtocolError::ValueError)?;
206                buffer.append(&mut price_as_u64.to_be_bytes().to_vec());
207            } else {
208                buffer.push(0);
209            }
210        }
211
212        // User defined properties
213        document_type
214            .properties()
215            .iter()
216            .try_for_each(|(field_name, property)| {
217                if let Some(value) = self.properties.get(field_name) {
218                    if value.is_null() {
219                        if property.required && !property.transient {
220                            Err(ProtocolError::DataContractError(
221                                DataContractError::MissingRequiredKey(
222                                    "a required field is not present".to_string(),
223                                ),
224                            ))
225                        } else {
226                            // dbg!("we pushed {} with 0", field_name);
227                            // We don't have something that wasn't required
228                            buffer.push(0);
229                            Ok(())
230                        }
231                    } else {
232                        if !property.required || property.transient {
233                            // dbg!("we added 1", field_name);
234                            buffer.push(1);
235                        }
236                        let value = if property.property_type.is_integer() {
237                            DocumentPropertyType::I64
238                                .encode_value_ref_with_size(value, property.required)
239                        } else {
240                            property
241                                .property_type
242                                .encode_value_ref_with_size(value, property.required)
243                        }?;
244
245                        // dbg!("we pushed {} with {}", field_name, hex::encode(&value));
246                        buffer.extend(value.as_slice());
247                        Ok(())
248                    }
249                } else if property.required && !property.transient {
250                    Err(ProtocolError::DataContractError(
251                        DataContractError::MissingRequiredKey(format!(
252                            "a required field {field_name} is not present"
253                        )),
254                    ))
255                } else {
256                    // dbg!("we pushed {} with 0", field_name);
257                    // We don't have something that wasn't required
258                    buffer.push(0);
259                    Ok(())
260                }
261            })?;
262
263        Ok(buffer)
264    }
265
266    /// Serializes the document.
267    ///
268    /// The serialization of a document follows the pattern:
269    /// id 32 bytes + owner_id 32 bytes + encoded values byte arrays
270    /// Serialize v1 will encode integers normally with their known size.
271    /// Otherwise it is almost identical to V0. V1 represents the original code.
272    fn serialize_v1(&self, document_type: DocumentTypeRef) -> Result<Vec<u8>, ProtocolError> {
273        let mut buffer: Vec<u8> = 1u64.encode_var_vec(); //version 1
274
275        // $id
276        buffer.extend(self.id.as_slice());
277
278        // $ownerId
279        buffer.extend(self.owner_id.as_slice());
280
281        // $revision
282        if let Some(revision) = self.revision {
283            buffer.extend(revision.encode_var_vec())
284        } else if document_type.requires_revision() {
285            buffer.extend((1 as Revision).encode_var_vec())
286        }
287
288        let mut bitwise_exists_flag: u16 = 0;
289
290        let mut time_fields_data_buffer = vec![];
291
292        // $createdAt
293        if let Some(created_at) = &self.created_at {
294            bitwise_exists_flag |= 1;
295            // dbg!("we pushed created at {}", hex::encode(created_at.to_be_bytes()));
296            time_fields_data_buffer.extend(created_at.to_be_bytes());
297        } else if document_type.required_fields().contains(CREATED_AT) {
298            return Err(ProtocolError::DataContractError(
299                DataContractError::MissingRequiredKey(
300                    "created at field is not present".to_string(),
301                ),
302            ));
303        }
304
305        // $updatedAt
306        if let Some(updated_at) = &self.updated_at {
307            bitwise_exists_flag |= 2;
308            // dbg!("we pushed updated at {}", hex::encode(updated_at.to_be_bytes()));
309            time_fields_data_buffer.extend(updated_at.to_be_bytes());
310        } else if document_type.required_fields().contains(UPDATED_AT) {
311            return Err(ProtocolError::DataContractError(
312                DataContractError::MissingRequiredKey(
313                    "updated at field is not present".to_string(),
314                ),
315            ));
316        }
317
318        // $transferredAt
319        if let Some(transferred_at) = &self.transferred_at {
320            bitwise_exists_flag |= 4;
321            // dbg!("we pushed transferred at {}", hex::encode(transferred_at.to_be_bytes()));
322            time_fields_data_buffer.extend(transferred_at.to_be_bytes());
323        } else if document_type.required_fields().contains(TRANSFERRED_AT) {
324            return Err(ProtocolError::DataContractError(
325                DataContractError::MissingRequiredKey(
326                    "transferred at field is not present".to_string(),
327                ),
328            ));
329        }
330
331        // $createdAtBlockHeight
332        if let Some(created_at_block_height) = &self.created_at_block_height {
333            bitwise_exists_flag |= 8;
334            time_fields_data_buffer.extend(created_at_block_height.to_be_bytes());
335        } else if document_type
336            .required_fields()
337            .contains(CREATED_AT_BLOCK_HEIGHT)
338        {
339            return Err(ProtocolError::DataContractError(
340                DataContractError::MissingRequiredKey(
341                    "created_at_block_height field is not present".to_string(),
342                ),
343            ));
344        }
345
346        // $updatedAtBlockHeight
347        if let Some(updated_at_block_height) = &self.updated_at_block_height {
348            bitwise_exists_flag |= 16;
349            time_fields_data_buffer.extend(updated_at_block_height.to_be_bytes());
350        } else if document_type
351            .required_fields()
352            .contains(UPDATED_AT_BLOCK_HEIGHT)
353        {
354            return Err(ProtocolError::DataContractError(
355                DataContractError::MissingRequiredKey(
356                    "updated_at_block_height field is not present".to_string(),
357                ),
358            ));
359        }
360
361        // $transferredAtBlockHeight
362        if let Some(transferred_at_block_height) = &self.transferred_at_block_height {
363            bitwise_exists_flag |= 32;
364            time_fields_data_buffer.extend(transferred_at_block_height.to_be_bytes());
365        } else if document_type
366            .required_fields()
367            .contains(TRANSFERRED_AT_BLOCK_HEIGHT)
368        {
369            return Err(ProtocolError::DataContractError(
370                DataContractError::MissingRequiredKey(
371                    "transferred_at_block_height field is not present".to_string(),
372                ),
373            ));
374        }
375
376        // $createdAtCoreBlockHeight
377        if let Some(created_at_core_block_height) = &self.created_at_core_block_height {
378            bitwise_exists_flag |= 64;
379            time_fields_data_buffer.extend(created_at_core_block_height.to_be_bytes());
380        } else if document_type
381            .required_fields()
382            .contains(CREATED_AT_CORE_BLOCK_HEIGHT)
383        {
384            return Err(ProtocolError::DataContractError(
385                DataContractError::MissingRequiredKey(
386                    "created_at_core_block_height field is not present".to_string(),
387                ),
388            ));
389        }
390
391        // $updatedAtCoreBlockHeight
392        if let Some(updated_at_core_block_height) = &self.updated_at_core_block_height {
393            bitwise_exists_flag |= 128;
394            time_fields_data_buffer.extend(updated_at_core_block_height.to_be_bytes());
395        } else if document_type
396            .required_fields()
397            .contains(UPDATED_AT_CORE_BLOCK_HEIGHT)
398        {
399            return Err(ProtocolError::DataContractError(
400                DataContractError::MissingRequiredKey(
401                    "updated_at_core_block_height field is not present".to_string(),
402                ),
403            ));
404        }
405
406        // $transferredAtCoreBlockHeight
407        if let Some(transferred_at_core_block_height) = &self.transferred_at_core_block_height {
408            bitwise_exists_flag |= 256;
409            time_fields_data_buffer.extend(transferred_at_core_block_height.to_be_bytes());
410        } else if document_type
411            .required_fields()
412            .contains(TRANSFERRED_AT_CORE_BLOCK_HEIGHT)
413        {
414            return Err(ProtocolError::DataContractError(
415                DataContractError::MissingRequiredKey(
416                    "transferred_at_core_block_height field is not present".to_string(),
417                ),
418            ));
419        }
420
421        buffer.extend(bitwise_exists_flag.to_be_bytes().as_slice());
422        buffer.append(&mut time_fields_data_buffer);
423
424        // Now we serialize the price which might not be necessary unless called for by the document type
425
426        if document_type.trade_mode().seller_sets_price() {
427            if let Some(price) = self.properties.get(PRICE) {
428                buffer.push(1);
429                let price_as_u64: u64 = price.to_integer().map_err(ProtocolError::ValueError)?;
430                buffer.append(&mut price_as_u64.to_be_bytes().to_vec());
431            } else {
432                buffer.push(0);
433            }
434        }
435
436        // User defined properties
437        document_type
438            .properties()
439            .iter()
440            .try_for_each(|(field_name, property)| {
441                if let Some(value) = self.properties.get(field_name) {
442                    if value.is_null() {
443                        if property.required && !property.transient {
444                            Err(ProtocolError::DataContractError(
445                                DataContractError::MissingRequiredKey(
446                                    "a required field is not present".to_string(),
447                                ),
448                            ))
449                        } else {
450                            // dbg!("we pushed {} with 0", field_name);
451                            // We don't have something that wasn't required
452                            buffer.push(0);
453                            Ok(())
454                        }
455                    } else {
456                        if !property.required || property.transient {
457                            // dbg!("we added 1", field_name);
458                            buffer.push(1);
459                        }
460                        let value = property
461                            .property_type
462                            .encode_value_ref_with_size(value, property.required)?;
463                        // dbg!("we pushed {} with {}", field_name, hex::encode(&value));
464                        buffer.extend(value.as_slice());
465                        Ok(())
466                    }
467                } else if property.required && !property.transient {
468                    Err(ProtocolError::DataContractError(
469                        DataContractError::MissingRequiredKey(format!(
470                            "a required field {field_name} is not present"
471                        )),
472                    ))
473                } else {
474                    // dbg!("we pushed {} with 0", field_name);
475                    // We don't have something that wasn't required
476                    buffer.push(0);
477                    Ok(())
478                }
479            })?;
480
481        Ok(buffer)
482    }
483
484    /// Serializes the document.
485    ///
486    /// The serialization of a document follows the pattern:
487    /// id 32 bytes + owner_id 32 bytes + encoded values byte arrays
488    /// Serialize v2 will encode the creator id as well.
489    fn serialize_v2(&self, document_type: DocumentTypeRef) -> Result<Vec<u8>, ProtocolError> {
490        let mut buffer: Vec<u8> = 2u64.encode_var_vec(); //version 2
491
492        // $id
493        buffer.extend(self.id.as_slice());
494
495        // $ownerId
496        buffer.extend(self.owner_id.as_slice());
497
498        if document_type.trade_mode() != TradeMode::None
499            || document_type.documents_transferable().is_transferable()
500        {
501            if let Some(creator_id) = self.creator_id {
502                buffer.push(1);
503                buffer.extend(creator_id.as_slice());
504            } else {
505                buffer.push(0);
506            }
507        }
508
509        // $revision
510        if let Some(revision) = self.revision {
511            buffer.extend(revision.encode_var_vec())
512        } else if document_type.requires_revision() {
513            buffer.extend((1 as Revision).encode_var_vec())
514        }
515
516        let mut bitwise_exists_flag: u16 = 0;
517
518        let mut time_fields_data_buffer = vec![];
519
520        // $createdAt
521        if let Some(created_at) = &self.created_at {
522            bitwise_exists_flag |= 1;
523            // dbg!("we pushed created at {}", hex::encode(created_at.to_be_bytes()));
524            time_fields_data_buffer.extend(created_at.to_be_bytes());
525        } else if document_type.required_fields().contains(CREATED_AT) {
526            return Err(ProtocolError::DataContractError(
527                DataContractError::MissingRequiredKey(
528                    "created at field is not present".to_string(),
529                ),
530            ));
531        }
532
533        // $updatedAt
534        if let Some(updated_at) = &self.updated_at {
535            bitwise_exists_flag |= 2;
536            // dbg!("we pushed updated at {}", hex::encode(updated_at.to_be_bytes()));
537            time_fields_data_buffer.extend(updated_at.to_be_bytes());
538        } else if document_type.required_fields().contains(UPDATED_AT) {
539            return Err(ProtocolError::DataContractError(
540                DataContractError::MissingRequiredKey(
541                    "updated at field is not present".to_string(),
542                ),
543            ));
544        }
545
546        // $transferredAt
547        if let Some(transferred_at) = &self.transferred_at {
548            bitwise_exists_flag |= 4;
549            // dbg!("we pushed transferred at {}", hex::encode(transferred_at.to_be_bytes()));
550            time_fields_data_buffer.extend(transferred_at.to_be_bytes());
551        } else if document_type.required_fields().contains(TRANSFERRED_AT) {
552            return Err(ProtocolError::DataContractError(
553                DataContractError::MissingRequiredKey(
554                    "transferred at field is not present".to_string(),
555                ),
556            ));
557        }
558
559        // $createdAtBlockHeight
560        if let Some(created_at_block_height) = &self.created_at_block_height {
561            bitwise_exists_flag |= 8;
562            time_fields_data_buffer.extend(created_at_block_height.to_be_bytes());
563        } else if document_type
564            .required_fields()
565            .contains(CREATED_AT_BLOCK_HEIGHT)
566        {
567            return Err(ProtocolError::DataContractError(
568                DataContractError::MissingRequiredKey(
569                    "created_at_block_height field is not present".to_string(),
570                ),
571            ));
572        }
573
574        // $updatedAtBlockHeight
575        if let Some(updated_at_block_height) = &self.updated_at_block_height {
576            bitwise_exists_flag |= 16;
577            time_fields_data_buffer.extend(updated_at_block_height.to_be_bytes());
578        } else if document_type
579            .required_fields()
580            .contains(UPDATED_AT_BLOCK_HEIGHT)
581        {
582            return Err(ProtocolError::DataContractError(
583                DataContractError::MissingRequiredKey(
584                    "updated_at_block_height field is not present".to_string(),
585                ),
586            ));
587        }
588
589        // $transferredAtBlockHeight
590        if let Some(transferred_at_block_height) = &self.transferred_at_block_height {
591            bitwise_exists_flag |= 32;
592            time_fields_data_buffer.extend(transferred_at_block_height.to_be_bytes());
593        } else if document_type
594            .required_fields()
595            .contains(TRANSFERRED_AT_BLOCK_HEIGHT)
596        {
597            return Err(ProtocolError::DataContractError(
598                DataContractError::MissingRequiredKey(
599                    "transferred_at_block_height field is not present".to_string(),
600                ),
601            ));
602        }
603
604        // $createdAtCoreBlockHeight
605        if let Some(created_at_core_block_height) = &self.created_at_core_block_height {
606            bitwise_exists_flag |= 64;
607            time_fields_data_buffer.extend(created_at_core_block_height.to_be_bytes());
608        } else if document_type
609            .required_fields()
610            .contains(CREATED_AT_CORE_BLOCK_HEIGHT)
611        {
612            return Err(ProtocolError::DataContractError(
613                DataContractError::MissingRequiredKey(
614                    "created_at_core_block_height field is not present".to_string(),
615                ),
616            ));
617        }
618
619        // $updatedAtCoreBlockHeight
620        if let Some(updated_at_core_block_height) = &self.updated_at_core_block_height {
621            bitwise_exists_flag |= 128;
622            time_fields_data_buffer.extend(updated_at_core_block_height.to_be_bytes());
623        } else if document_type
624            .required_fields()
625            .contains(UPDATED_AT_CORE_BLOCK_HEIGHT)
626        {
627            return Err(ProtocolError::DataContractError(
628                DataContractError::MissingRequiredKey(
629                    "updated_at_core_block_height field is not present".to_string(),
630                ),
631            ));
632        }
633
634        // $transferredAtCoreBlockHeight
635        if let Some(transferred_at_core_block_height) = &self.transferred_at_core_block_height {
636            bitwise_exists_flag |= 256;
637            time_fields_data_buffer.extend(transferred_at_core_block_height.to_be_bytes());
638        } else if document_type
639            .required_fields()
640            .contains(TRANSFERRED_AT_CORE_BLOCK_HEIGHT)
641        {
642            return Err(ProtocolError::DataContractError(
643                DataContractError::MissingRequiredKey(
644                    "transferred_at_core_block_height field is not present".to_string(),
645                ),
646            ));
647        }
648
649        buffer.extend(bitwise_exists_flag.to_be_bytes().as_slice());
650        buffer.append(&mut time_fields_data_buffer);
651
652        // Now we serialize the price which might not be necessary unless called for by the document type
653
654        if document_type.trade_mode().seller_sets_price() {
655            if let Some(price) = self.properties.get(PRICE) {
656                buffer.push(1);
657                let price_as_u64: u64 = price.to_integer().map_err(ProtocolError::ValueError)?;
658                buffer.append(&mut price_as_u64.to_be_bytes().to_vec());
659            } else {
660                buffer.push(0);
661            }
662        }
663
664        // User defined properties
665        document_type
666            .properties()
667            .iter()
668            .try_for_each(|(field_name, property)| {
669                if let Some(value) = self.properties.get(field_name) {
670                    if value.is_null() {
671                        if property.required && !property.transient {
672                            Err(ProtocolError::DataContractError(
673                                DataContractError::MissingRequiredKey(
674                                    "a required field is not present".to_string(),
675                                ),
676                            ))
677                        } else {
678                            // dbg!("we pushed {} with 0", field_name);
679                            // We don't have something that wasn't required
680                            buffer.push(0);
681                            Ok(())
682                        }
683                    } else {
684                        if !property.required || property.transient {
685                            // dbg!("we added 1", field_name);
686                            buffer.push(1);
687                        }
688                        let value = property
689                            .property_type
690                            .encode_value_ref_with_size(value, property.required)?;
691                        // dbg!("we pushed {} with {}", field_name, hex::encode(&value));
692                        buffer.extend(value.as_slice());
693                        Ok(())
694                    }
695                } else if property.required && !property.transient {
696                    Err(ProtocolError::DataContractError(
697                        DataContractError::MissingRequiredKey(format!(
698                            "a required field {field_name} is not present"
699                        )),
700                    ))
701                } else {
702                    // dbg!("we pushed {} with 0", field_name);
703                    // We don't have something that wasn't required
704                    buffer.push(0);
705                    Ok(())
706                }
707            })?;
708
709        Ok(buffer)
710    }
711}
712
713impl DocumentPlatformDeserializationMethodsV0 for DocumentV0 {
714    /// Reads a serialized document and creates a Document from it.
715    fn from_bytes_v0(
716        serialized_document: &[u8],
717        document_type: DocumentTypeRef,
718        _platform_version: &PlatformVersion,
719    ) -> Result<Self, DataContractError> {
720        let mut buf = BufReader::new(serialized_document);
721        if serialized_document.len() < 64 {
722            return Err(DataContractError::DecodingDocumentError(
723                DecodingError::new(
724                    "serialized document is too small, must have id and owner id".to_string(),
725                ),
726            ));
727        }
728
729        // $id
730        let mut id = [0; 32];
731        buf.read_exact(&mut id).map_err(|_| {
732            DataContractError::DecodingDocumentError(DecodingError::new(
733                "error reading from serialized document for id".to_string(),
734            ))
735        })?;
736
737        // $ownerId
738        let mut owner_id = [0; 32];
739        buf.read_exact(&mut owner_id).map_err(|_| {
740            DataContractError::DecodingDocumentError(DecodingError::new(
741                "error reading from serialized document for owner id".to_string(),
742            ))
743        })?;
744
745        // $revision
746        // if the document type is mutable then we should deserialize the revision
747        let revision: Option<Revision> = if document_type.requires_revision() {
748            let revision = buf.read_varint().map_err(|_| {
749                DataContractError::DecodingDocumentError(DecodingError::new(
750                    "error reading revision from serialized document for revision".to_string(),
751                ))
752            })?;
753            Some(revision)
754        } else {
755            None
756        };
757
758        let timestamp_flags = buf.read_u16::<BigEndian>().map_err(|_| {
759            DataContractError::CorruptedSerialization(
760                "error reading timestamp flags from serialized document".to_string(),
761            )
762        })?;
763
764        let created_at = if timestamp_flags & 1 > 0 {
765            Some(buf.read_u64::<BigEndian>().map_err(|_| {
766                DataContractError::CorruptedSerialization(
767                    "error reading created_at timestamp from serialized document".to_string(),
768                )
769            })?)
770        } else {
771            None
772        };
773
774        let updated_at = if timestamp_flags & 2 > 0 {
775            Some(buf.read_u64::<BigEndian>().map_err(|_| {
776                DataContractError::CorruptedSerialization(
777                    "error reading updated_at timestamp from serialized document".to_string(),
778                )
779            })?)
780        } else {
781            None
782        };
783
784        let transferred_at = if timestamp_flags & 4 > 0 {
785            Some(buf.read_u64::<BigEndian>().map_err(|_| {
786                DataContractError::CorruptedSerialization(
787                    "error reading transferred_at timestamp from serialized document".to_string(),
788                )
789            })?)
790        } else {
791            None
792        };
793
794        let created_at_block_height = if timestamp_flags & 8 > 0 {
795            Some(buf.read_u64::<BigEndian>().map_err(|_| {
796                DataContractError::CorruptedSerialization(
797                    "error reading created_at_block_height from serialized document".to_string(),
798                )
799            })?)
800        } else {
801            None
802        };
803
804        let updated_at_block_height = if timestamp_flags & 16 > 0 {
805            Some(buf.read_u64::<BigEndian>().map_err(|_| {
806                DataContractError::CorruptedSerialization(
807                    "error reading updated_at_block_height from serialized document".to_string(),
808                )
809            })?)
810        } else {
811            None
812        };
813
814        let transferred_at_block_height = if timestamp_flags & 32 > 0 {
815            Some(buf.read_u64::<BigEndian>().map_err(|_| {
816                DataContractError::CorruptedSerialization(
817                    "error reading transferred_at_block_height from serialized document"
818                        .to_string(),
819                )
820            })?)
821        } else {
822            None
823        };
824
825        let created_at_core_block_height = if timestamp_flags & 64 > 0 {
826            Some(buf.read_u32::<BigEndian>().map_err(|_| {
827                DataContractError::CorruptedSerialization(
828                    "error reading created_at_core_block_height from serialized document"
829                        .to_string(),
830                )
831            })?)
832        } else {
833            None
834        };
835
836        let updated_at_core_block_height = if timestamp_flags & 128 > 0 {
837            Some(buf.read_u32::<BigEndian>().map_err(|_| {
838                DataContractError::CorruptedSerialization(
839                    "error reading updated_at_core_block_height from serialized document"
840                        .to_string(),
841                )
842            })?)
843        } else {
844            None
845        };
846
847        let transferred_at_core_block_height = if timestamp_flags & 256 > 0 {
848            Some(buf.read_u32::<BigEndian>().map_err(|_| {
849                DataContractError::CorruptedSerialization(
850                    "error reading updated_at_core_block_height from serialized document"
851                        .to_string(),
852                )
853            })?)
854        } else {
855            None
856        };
857
858        // Now we deserialize the price which might not be necessary unless called for by the document type
859
860        let price = if document_type.trade_mode().seller_sets_price() {
861            let has_price = buf.read_u8().map_err(|_| {
862                DataContractError::CorruptedSerialization(
863                    "error reading has price bool from serialized document".to_string(),
864                )
865            })?;
866            if has_price > 0 {
867                let price = buf.read_u64::<BigEndian>().map_err(|_| {
868                    DataContractError::CorruptedSerialization(
869                        "error reading price u64 from serialized document".to_string(),
870                    )
871                })?;
872                Some(price)
873            } else {
874                None
875            }
876        } else {
877            None
878        };
879
880        let mut finished_buffer = false;
881
882        let mut properties = document_type
883            .properties()
884            .iter()
885            .filter_map(|(key, property)| {
886                if finished_buffer {
887                    return if property.required && !property.transient {
888                        Some(Err(DataContractError::CorruptedSerialization(
889                            "required field after finished buffer".to_string(),
890                        )))
891                    } else {
892                        None
893                    };
894                }
895
896                // In version 0 all integers are encoded as I64 (in theory)
897                let read_value = if property.property_type.is_integer() {
898                    DocumentPropertyType::I64
899                        .read_optionally_from(&mut buf, property.required & !property.transient)
900                } else {
901                    property
902                        .property_type
903                        .read_optionally_from(&mut buf, property.required & !property.transient)
904                };
905
906                match read_value {
907                    Ok(read_value) => {
908                        finished_buffer |= read_value.1;
909                        read_value.0.map(|read_value| Ok((key.clone(), read_value)))
910                    }
911                    Err(e) => Some(Err(e)),
912                }
913            })
914            .collect::<Result<BTreeMap<String, Value>, DataContractError>>()?;
915
916        if let Some(price) = price {
917            properties.insert(PRICE.to_string(), price.into());
918        }
919
920        Ok(DocumentV0 {
921            id: Identifier::new(id),
922            properties,
923            owner_id: Identifier::new(owner_id),
924            revision,
925            created_at,
926            updated_at,
927            transferred_at,
928            created_at_block_height,
929            updated_at_block_height,
930            transferred_at_block_height,
931            created_at_core_block_height,
932            updated_at_core_block_height,
933            transferred_at_core_block_height,
934            creator_id: None,
935        })
936    }
937
938    /// Reads a serialized document and creates a Document from it.
939    fn from_bytes_v1(
940        serialized_document: &[u8],
941        document_type: DocumentTypeRef,
942        _platform_version: &PlatformVersion,
943    ) -> Result<Self, DataContractError> {
944        let mut buf = BufReader::new(serialized_document);
945        if serialized_document.len() < 64 {
946            return Err(DataContractError::DecodingDocumentError(
947                DecodingError::new(
948                    "serialized document is too small, must have id and owner id".to_string(),
949                ),
950            ));
951        }
952
953        // $id
954        let mut id = [0; 32];
955        buf.read_exact(&mut id).map_err(|_| {
956            DataContractError::DecodingDocumentError(DecodingError::new(
957                "error reading from serialized document for id".to_string(),
958            ))
959        })?;
960
961        // $ownerId
962        let mut owner_id = [0; 32];
963        buf.read_exact(&mut owner_id).map_err(|_| {
964            DataContractError::DecodingDocumentError(DecodingError::new(
965                "error reading from serialized document for owner id".to_string(),
966            ))
967        })?;
968
969        // $revision
970        // if the document type is mutable then we should deserialize the revision
971        let revision: Option<Revision> = if document_type.requires_revision() {
972            let revision = buf.read_varint().map_err(|_| {
973                DataContractError::DecodingDocumentError(DecodingError::new(
974                    "error reading revision from serialized document for revision".to_string(),
975                ))
976            })?;
977            Some(revision)
978        } else {
979            None
980        };
981
982        let timestamp_flags = buf.read_u16::<BigEndian>().map_err(|_| {
983            DataContractError::CorruptedSerialization(
984                "error reading timestamp flags from serialized document".to_string(),
985            )
986        })?;
987
988        let created_at = if timestamp_flags & 1 > 0 {
989            Some(buf.read_u64::<BigEndian>().map_err(|_| {
990                DataContractError::CorruptedSerialization(
991                    "error reading created_at timestamp from serialized document".to_string(),
992                )
993            })?)
994        } else {
995            None
996        };
997
998        let updated_at = if timestamp_flags & 2 > 0 {
999            Some(buf.read_u64::<BigEndian>().map_err(|_| {
1000                DataContractError::CorruptedSerialization(
1001                    "error reading updated_at timestamp from serialized document".to_string(),
1002                )
1003            })?)
1004        } else {
1005            None
1006        };
1007
1008        let transferred_at = if timestamp_flags & 4 > 0 {
1009            Some(buf.read_u64::<BigEndian>().map_err(|_| {
1010                DataContractError::CorruptedSerialization(
1011                    "error reading transferred_at timestamp from serialized document".to_string(),
1012                )
1013            })?)
1014        } else {
1015            None
1016        };
1017
1018        let created_at_block_height = if timestamp_flags & 8 > 0 {
1019            Some(buf.read_u64::<BigEndian>().map_err(|_| {
1020                DataContractError::CorruptedSerialization(
1021                    "error reading created_at_block_height from serialized document".to_string(),
1022                )
1023            })?)
1024        } else {
1025            None
1026        };
1027
1028        let updated_at_block_height = if timestamp_flags & 16 > 0 {
1029            Some(buf.read_u64::<BigEndian>().map_err(|_| {
1030                DataContractError::CorruptedSerialization(
1031                    "error reading updated_at_block_height from serialized document".to_string(),
1032                )
1033            })?)
1034        } else {
1035            None
1036        };
1037
1038        let transferred_at_block_height = if timestamp_flags & 32 > 0 {
1039            Some(buf.read_u64::<BigEndian>().map_err(|_| {
1040                DataContractError::CorruptedSerialization(
1041                    "error reading transferred_at_block_height from serialized document"
1042                        .to_string(),
1043                )
1044            })?)
1045        } else {
1046            None
1047        };
1048
1049        let created_at_core_block_height = if timestamp_flags & 64 > 0 {
1050            Some(buf.read_u32::<BigEndian>().map_err(|_| {
1051                DataContractError::CorruptedSerialization(
1052                    "error reading created_at_core_block_height from serialized document"
1053                        .to_string(),
1054                )
1055            })?)
1056        } else {
1057            None
1058        };
1059
1060        let updated_at_core_block_height = if timestamp_flags & 128 > 0 {
1061            Some(buf.read_u32::<BigEndian>().map_err(|_| {
1062                DataContractError::CorruptedSerialization(
1063                    "error reading updated_at_core_block_height from serialized document"
1064                        .to_string(),
1065                )
1066            })?)
1067        } else {
1068            None
1069        };
1070
1071        let transferred_at_core_block_height = if timestamp_flags & 256 > 0 {
1072            Some(buf.read_u32::<BigEndian>().map_err(|_| {
1073                DataContractError::CorruptedSerialization(
1074                    "error reading updated_at_core_block_height from serialized document"
1075                        .to_string(),
1076                )
1077            })?)
1078        } else {
1079            None
1080        };
1081
1082        // Now we deserialize the price which might not be necessary unless called for by the document type
1083
1084        let price = if document_type.trade_mode().seller_sets_price() {
1085            let has_price = buf.read_u8().map_err(|_| {
1086                DataContractError::CorruptedSerialization(
1087                    "error reading has price bool from serialized document".to_string(),
1088                )
1089            })?;
1090            if has_price > 0 {
1091                let price = buf.read_u64::<BigEndian>().map_err(|_| {
1092                    DataContractError::CorruptedSerialization(
1093                        "error reading price u64 from serialized document".to_string(),
1094                    )
1095                })?;
1096                Some(price)
1097            } else {
1098                None
1099            }
1100        } else {
1101            None
1102        };
1103
1104        let mut finished_buffer = false;
1105
1106        let mut properties = document_type
1107            .properties()
1108            .iter()
1109            .filter_map(|(key, property)| {
1110                if finished_buffer {
1111                    return if property.required && !property.transient {
1112                        Some(Err(DataContractError::CorruptedSerialization(
1113                            "required field after finished buffer".to_string(),
1114                        )))
1115                    } else {
1116                        None
1117                    };
1118                }
1119                let read_value = property
1120                    .property_type
1121                    .read_optionally_from(&mut buf, property.required & !property.transient);
1122
1123                match read_value {
1124                    Ok(read_value) => {
1125                        finished_buffer |= read_value.1;
1126                        read_value.0.map(|read_value| Ok((key.clone(), read_value)))
1127                    }
1128                    Err(e) => Some(Err(e)),
1129                }
1130            })
1131            .collect::<Result<BTreeMap<String, Value>, DataContractError>>()?;
1132
1133        if let Some(price) = price {
1134            properties.insert(PRICE.to_string(), price.into());
1135        }
1136
1137        Ok(DocumentV0 {
1138            id: Identifier::new(id),
1139            properties,
1140            owner_id: Identifier::new(owner_id),
1141            revision,
1142            created_at,
1143            updated_at,
1144            transferred_at,
1145            created_at_block_height,
1146            updated_at_block_height,
1147            transferred_at_block_height,
1148            created_at_core_block_height,
1149            updated_at_core_block_height,
1150            transferred_at_core_block_height,
1151            creator_id: None,
1152        })
1153    }
1154
1155    /// Reads a serialized document and creates a Document from it.
1156    fn from_bytes_v2(
1157        serialized_document: &[u8],
1158        document_type: DocumentTypeRef,
1159        _platform_version: &PlatformVersion,
1160    ) -> Result<Self, DataContractError> {
1161        let mut buf = BufReader::new(serialized_document);
1162        if serialized_document.len() < 64 {
1163            return Err(DataContractError::DecodingDocumentError(
1164                DecodingError::new(
1165                    "serialized document is too small, must have id and owner id".to_string(),
1166                ),
1167            ));
1168        }
1169
1170        // $id
1171        let mut id = [0; 32];
1172        buf.read_exact(&mut id).map_err(|_| {
1173            DataContractError::DecodingDocumentError(DecodingError::new(
1174                "error reading from serialized document for id".to_string(),
1175            ))
1176        })?;
1177
1178        // $ownerId
1179        let mut owner_id = [0; 32];
1180        buf.read_exact(&mut owner_id).map_err(|_| {
1181            DataContractError::DecodingDocumentError(DecodingError::new(
1182                "error reading from serialized document for owner id".to_string(),
1183            ))
1184        })?;
1185
1186        // $creatorId
1187        let creator_id: Option<Identifier> = if document_type.trade_mode() != TradeMode::None
1188            || document_type.documents_transferable().is_transferable()
1189        {
1190            let has_creator_id = buf.read_u8().map_err(|_| {
1191                DataContractError::CorruptedSerialization(
1192                    "error reading has creator id bool from serialized document".to_string(),
1193                )
1194            })?;
1195            if has_creator_id > 0 {
1196                // $creatorId
1197                let mut known_owner_id = [0; 32];
1198                buf.read_exact(&mut known_owner_id).map_err(|_| {
1199                    DataContractError::DecodingDocumentError(DecodingError::new(
1200                        "error reading from serialized document for creator id".to_string(),
1201                    ))
1202                })?;
1203                Some(known_owner_id.into())
1204            } else {
1205                None
1206            }
1207        } else {
1208            None
1209        };
1210
1211        // $revision
1212        // if the document type is mutable then we should deserialize the revision
1213        let revision: Option<Revision> = if document_type.requires_revision() {
1214            let revision = buf.read_varint().map_err(|_| {
1215                DataContractError::DecodingDocumentError(DecodingError::new(
1216                    "error reading revision from serialized document for revision".to_string(),
1217                ))
1218            })?;
1219            Some(revision)
1220        } else {
1221            None
1222        };
1223
1224        let timestamp_flags = buf.read_u16::<BigEndian>().map_err(|_| {
1225            DataContractError::CorruptedSerialization(
1226                "error reading timestamp flags from serialized document".to_string(),
1227            )
1228        })?;
1229
1230        let created_at = if timestamp_flags & 1 > 0 {
1231            Some(buf.read_u64::<BigEndian>().map_err(|_| {
1232                DataContractError::CorruptedSerialization(
1233                    "error reading created_at timestamp from serialized document".to_string(),
1234                )
1235            })?)
1236        } else {
1237            None
1238        };
1239
1240        let updated_at = if timestamp_flags & 2 > 0 {
1241            Some(buf.read_u64::<BigEndian>().map_err(|_| {
1242                DataContractError::CorruptedSerialization(
1243                    "error reading updated_at timestamp from serialized document".to_string(),
1244                )
1245            })?)
1246        } else {
1247            None
1248        };
1249
1250        let transferred_at = if timestamp_flags & 4 > 0 {
1251            Some(buf.read_u64::<BigEndian>().map_err(|_| {
1252                DataContractError::CorruptedSerialization(
1253                    "error reading transferred_at timestamp from serialized document".to_string(),
1254                )
1255            })?)
1256        } else {
1257            None
1258        };
1259
1260        let created_at_block_height = if timestamp_flags & 8 > 0 {
1261            Some(buf.read_u64::<BigEndian>().map_err(|_| {
1262                DataContractError::CorruptedSerialization(
1263                    "error reading created_at_block_height from serialized document".to_string(),
1264                )
1265            })?)
1266        } else {
1267            None
1268        };
1269
1270        let updated_at_block_height = if timestamp_flags & 16 > 0 {
1271            Some(buf.read_u64::<BigEndian>().map_err(|_| {
1272                DataContractError::CorruptedSerialization(
1273                    "error reading updated_at_block_height from serialized document".to_string(),
1274                )
1275            })?)
1276        } else {
1277            None
1278        };
1279
1280        let transferred_at_block_height = if timestamp_flags & 32 > 0 {
1281            Some(buf.read_u64::<BigEndian>().map_err(|_| {
1282                DataContractError::CorruptedSerialization(
1283                    "error reading transferred_at_block_height from serialized document"
1284                        .to_string(),
1285                )
1286            })?)
1287        } else {
1288            None
1289        };
1290
1291        let created_at_core_block_height = if timestamp_flags & 64 > 0 {
1292            Some(buf.read_u32::<BigEndian>().map_err(|_| {
1293                DataContractError::CorruptedSerialization(
1294                    "error reading created_at_core_block_height from serialized document"
1295                        .to_string(),
1296                )
1297            })?)
1298        } else {
1299            None
1300        };
1301
1302        let updated_at_core_block_height = if timestamp_flags & 128 > 0 {
1303            Some(buf.read_u32::<BigEndian>().map_err(|_| {
1304                DataContractError::CorruptedSerialization(
1305                    "error reading updated_at_core_block_height from serialized document"
1306                        .to_string(),
1307                )
1308            })?)
1309        } else {
1310            None
1311        };
1312
1313        let transferred_at_core_block_height = if timestamp_flags & 256 > 0 {
1314            Some(buf.read_u32::<BigEndian>().map_err(|_| {
1315                DataContractError::CorruptedSerialization(
1316                    "error reading updated_at_core_block_height from serialized document"
1317                        .to_string(),
1318                )
1319            })?)
1320        } else {
1321            None
1322        };
1323
1324        // Now we deserialize the price which might not be necessary unless called for by the document type
1325
1326        let price = if document_type.trade_mode().seller_sets_price() {
1327            let has_price = buf.read_u8().map_err(|_| {
1328                DataContractError::CorruptedSerialization(
1329                    "error reading has price bool from serialized document".to_string(),
1330                )
1331            })?;
1332            if has_price > 0 {
1333                let price = buf.read_u64::<BigEndian>().map_err(|_| {
1334                    DataContractError::CorruptedSerialization(
1335                        "error reading price u64 from serialized document".to_string(),
1336                    )
1337                })?;
1338                Some(price)
1339            } else {
1340                None
1341            }
1342        } else {
1343            None
1344        };
1345
1346        let mut finished_buffer = false;
1347
1348        let mut properties = document_type
1349            .properties()
1350            .iter()
1351            .filter_map(|(key, property)| {
1352                if finished_buffer {
1353                    return if property.required && !property.transient {
1354                        Some(Err(DataContractError::CorruptedSerialization(
1355                            "required field after finished buffer".to_string(),
1356                        )))
1357                    } else {
1358                        None
1359                    };
1360                }
1361                let read_value = property
1362                    .property_type
1363                    .read_optionally_from(&mut buf, property.required & !property.transient);
1364
1365                match read_value {
1366                    Ok(read_value) => {
1367                        finished_buffer |= read_value.1;
1368                        read_value.0.map(|read_value| Ok((key.clone(), read_value)))
1369                    }
1370                    Err(e) => Some(Err(e)),
1371                }
1372            })
1373            .collect::<Result<BTreeMap<String, Value>, DataContractError>>()?;
1374
1375        if let Some(price) = price {
1376            properties.insert(PRICE.to_string(), price.into());
1377        }
1378
1379        Ok(DocumentV0 {
1380            id: Identifier::new(id),
1381            properties,
1382            owner_id: Identifier::new(owner_id),
1383            revision,
1384            created_at,
1385            updated_at,
1386            transferred_at,
1387            created_at_block_height,
1388            updated_at_block_height,
1389            transferred_at_block_height,
1390            created_at_core_block_height,
1391            updated_at_core_block_height,
1392            transferred_at_core_block_height,
1393            creator_id,
1394        })
1395    }
1396}
1397
1398impl DocumentPlatformConversionMethodsV0 for DocumentV0 {
1399    /// Serializes the document.
1400    ///
1401    /// The serialization of a document follows the pattern:
1402    /// id 32 bytes + owner_id 32 bytes + encoded values byte arrays
1403    fn serialize(
1404        &self,
1405        document_type: DocumentTypeRef,
1406        contract: &DataContract,
1407        platform_version: &PlatformVersion,
1408    ) -> Result<Vec<u8>, ProtocolError> {
1409        if matches!(contract, DataContract::V0(_))
1410            || matches!(contract.config(), DataContractConfig::V0(_))
1411        {
1412            // Any data contract in version 0 should always serialize documents in version 0
1413            // This is because integers in such a data contract if made through normal versioning should always
1414            // be i64
1415            // While it's possible in theory maybe that they are not i64 using serialize_v0
1416            // will encode all integers as i64.
1417            self.serialize_v0(document_type)
1418        } else {
1419            match platform_version
1420                .dpp
1421                .document_versions
1422                .document_serialization_version
1423                .default_current_version
1424            {
1425                0 => self.serialize_v0(document_type),
1426                // Version 1 coincides with protocol version 9, which contains tokens, new document types,
1427                // and most importantly different integer types.
1428                // Document types now have properties that are known to be things like u8, i32 etc.
1429                1 => self.serialize_v1(document_type),
1430                2 => self.serialize_v2(document_type),
1431                version => Err(ProtocolError::UnknownVersionMismatch {
1432                    method: "DocumentV0::serialize".to_string(),
1433                    known_versions: vec![0, 1, 2],
1434                    received: version,
1435                }),
1436            }
1437        }
1438    }
1439
1440    fn serialize_specific_version(
1441        &self,
1442        document_type: DocumentTypeRef,
1443        contract: &DataContract,
1444        feature_version: FeatureVersion,
1445    ) -> Result<Vec<u8>, ProtocolError> {
1446        if (matches!(contract, DataContract::V0(_))
1447            || matches!(contract.config(), DataContractConfig::V0(_)))
1448            && feature_version != 0
1449        {
1450            // Any data contract in version 0 should always serialize documents in version 0
1451            // This is because integers in such a data contract if made through normal versioning should always
1452            // be i64
1453            // While it's possible in theory maybe that they are not i64 using serialize_v0
1454            // will encode all integers as i64.
1455            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()));
1456        };
1457        match feature_version {
1458            0 => self.serialize_v0(document_type),
1459            1 => self.serialize_v1(document_type),
1460            2 => self.serialize_v2(document_type),
1461            version => Err(ProtocolError::UnknownVersionMismatch {
1462                method: "DocumentV0::serialize".to_string(),
1463                known_versions: vec![0, 1, 2],
1464                received: version,
1465            }),
1466        }
1467    }
1468
1469    /// Reads a serialized document and creates a DocumentV0 from it.
1470    fn from_bytes(
1471        mut serialized_document: &[u8],
1472        document_type: DocumentTypeRef,
1473        platform_version: &PlatformVersion,
1474    ) -> Result<Self, ProtocolError> {
1475        let serialized_version = serialized_document.read_varint().map_err(|_| {
1476            DataContractError::DecodingDocumentError(DecodingError::new(
1477                "error reading revision from serialized document for revision".to_string(),
1478            ))
1479        })?;
1480        match serialized_version {
1481            0 => {
1482                match DocumentV0::from_bytes_v0(
1483                    serialized_document,
1484                    document_type,
1485                    platform_version,
1486                )
1487                .map_err(ProtocolError::DataContractError)
1488                {
1489                    Ok(document) => Ok(document),
1490                    Err(first_err) => {
1491                        // let's try decoding in V1 just to be safe
1492                        // Version 0 will decode all integers as I64
1493                        // Version 1 will decode all integers properly
1494                        // When version was 0 used (protocol version 1 to 8) integers other than I64
1495                        // existed, but were probably never used, which is why we try v1 just to be safe
1496                        match DocumentV0::from_bytes_v1(
1497                            serialized_document,
1498                            document_type,
1499                            platform_version,
1500                        ) {
1501                            Ok(document_from_version_1_deserialization) => {
1502                                Ok(document_from_version_1_deserialization)
1503                            }
1504                            Err(_) => Err(first_err),
1505                        }
1506                    }
1507                }
1508            }
1509            1 => DocumentV0::from_bytes_v1(serialized_document, document_type, platform_version)
1510                .map_err(ProtocolError::DataContractError),
1511            2 => DocumentV0::from_bytes_v2(serialized_document, document_type, platform_version)
1512                .map_err(ProtocolError::DataContractError),
1513            version => Err(ProtocolError::UnknownVersionMismatch {
1514                method: "Document::from_bytes (deserialization)".to_string(),
1515                known_versions: vec![0, 1, 2],
1516                received: version,
1517            }),
1518        }
1519    }
1520
1521    /// Reads a serialized document and creates a DocumentV0 from it.
1522    #[cfg(feature = "validation")]
1523    fn from_bytes_in_consensus(
1524        mut serialized_document: &[u8],
1525        document_type: DocumentTypeRef,
1526        platform_version: &PlatformVersion,
1527    ) -> Result<ConsensusValidationResult<Self>, ProtocolError> {
1528        let serialized_version = serialized_document.read_varint().map_err(|_| {
1529            DataContractError::DecodingDocumentError(DecodingError::new(
1530                "error reading revision from serialized document for revision".to_string(),
1531            ))
1532        })?;
1533        match serialized_version {
1534            0 => {
1535                match DocumentV0::from_bytes_v0(
1536                    serialized_document,
1537                    document_type,
1538                    platform_version,
1539                ) {
1540                    Ok(document) => Ok(ConsensusValidationResult::new_with_data(document)),
1541                    Err(first_err) => {
1542                        // let's try decoding in V1 just to be safe
1543                        // Version 0 will decode all integers as I64
1544                        // Version 1 will decode all integers properly
1545                        // When version was 0 used (protocol version 1 to 8) integers other than I64
1546                        // existed, but were probably never used, which is why we try v1 just to be safe
1547                        match DocumentV0::from_bytes_v1(
1548                            serialized_document,
1549                            document_type,
1550                            platform_version,
1551                        ) {
1552                            Ok(document_from_version_1_deserialization) => {
1553                                Ok(ConsensusValidationResult::new_with_data(
1554                                    document_from_version_1_deserialization,
1555                                ))
1556                            }
1557                            Err(_) => Ok(ConsensusValidationResult::new_with_error(
1558                                ConsensusError::BasicError(BasicError::ContractError(first_err)),
1559                            )),
1560                        }
1561                    }
1562                }
1563            }
1564            1 => {
1565                match DocumentV0::from_bytes_v1(
1566                    serialized_document,
1567                    document_type,
1568                    platform_version,
1569                ) {
1570                    Ok(document) => Ok(ConsensusValidationResult::new_with_data(document)),
1571                    Err(err) => Ok(ConsensusValidationResult::new_with_error(
1572                        ConsensusError::BasicError(BasicError::ContractError(err)),
1573                    )),
1574                }
1575            }
1576            2 => {
1577                match DocumentV0::from_bytes_v2(
1578                    serialized_document,
1579                    document_type,
1580                    platform_version,
1581                ) {
1582                    Ok(document) => Ok(ConsensusValidationResult::new_with_data(document)),
1583                    Err(err) => Ok(ConsensusValidationResult::new_with_error(
1584                        ConsensusError::BasicError(BasicError::ContractError(err)),
1585                    )),
1586                }
1587            }
1588            version => Err(ProtocolError::UnknownVersionMismatch {
1589                method: "Document::from_bytes (deserialization)".to_string(),
1590                known_versions: vec![0, 1, 2],
1591                received: version,
1592            }),
1593        }
1594    }
1595}
1596
1597#[cfg(test)]
1598mod tests {
1599    use super::*;
1600    use crate::data_contract::accessors::v0::DataContractV0Getters;
1601    use crate::data_contract::document_type::random_document::CreateRandomDocument;
1602    use crate::tests::json_document::json_document_to_contract;
1603    use integer_encoding::VarInt;
1604    use platform_version::version::PlatformVersion;
1605
1606    // ----------------------------------------------------------------
1607    // Helper: load the dashpay contract and return the contract plus a
1608    // DocumentTypeRef for the given document type name.
1609    // ----------------------------------------------------------------
1610    fn dashpay_contract_and_type(
1611        platform_version: &PlatformVersion,
1612    ) -> (crate::prelude::DataContract, String) {
1613        let contract = json_document_to_contract(
1614            "../rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json",
1615            false,
1616            platform_version,
1617        )
1618        .expect("expected to load dashpay contract");
1619        (contract, "contactRequest".to_string())
1620    }
1621
1622    fn family_contract(platform_version: &PlatformVersion) -> crate::prelude::DataContract {
1623        json_document_to_contract(
1624            "../rs-drive/tests/supporting_files/contract/family/family-contract.json",
1625            false,
1626            platform_version,
1627        )
1628        .expect("expected to load family contract")
1629    }
1630
1631    fn withdrawals_contract(platform_version: &PlatformVersion) -> crate::prelude::DataContract {
1632        json_document_to_contract(
1633            "../rs-drive/tests/supporting_files/contract/withdrawals/withdrawals-contract.json",
1634            false,
1635            platform_version,
1636        )
1637        .expect("expected to load withdrawals contract")
1638    }
1639
1640    // ================================================================
1641    //  Round-trip: serialize then deserialize, expect equality
1642    // ================================================================
1643
1644    #[test]
1645    fn round_trip_serialize_v0_dashpay_contact_request() {
1646        let platform_version = PlatformVersion::first();
1647        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1648        let document_type = contract
1649            .document_type_for_name(&type_name)
1650            .expect("expected document type");
1651
1652        let document = document_type
1653            .random_document(Some(42), platform_version)
1654            .expect("expected random document");
1655
1656        let doc_v0 = match &document {
1657            crate::document::Document::V0(d) => d,
1658        };
1659
1660        let serialized = doc_v0
1661            .serialize(document_type, &contract, platform_version)
1662            .expect("serialize should succeed");
1663
1664        let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1665            .expect("from_bytes should succeed");
1666
1667        assert_eq!(*doc_v0, deserialized);
1668    }
1669
1670    #[test]
1671    fn round_trip_serialize_v0_family_person() {
1672        let platform_version = PlatformVersion::first();
1673        let contract = family_contract(platform_version);
1674        let document_type = contract
1675            .document_type_for_name("person")
1676            .expect("expected person document type");
1677
1678        for seed in 0..20u64 {
1679            let document = document_type
1680                .random_document(Some(seed), platform_version)
1681                .expect("expected random document");
1682            let doc_v0 = match &document {
1683                crate::document::Document::V0(d) => d,
1684            };
1685            let serialized = doc_v0
1686                .serialize(document_type, &contract, platform_version)
1687                .expect("serialize should succeed");
1688            let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1689                .expect("from_bytes should succeed");
1690            assert_eq!(*doc_v0, deserialized, "round-trip failed for seed {seed}");
1691        }
1692    }
1693
1694    #[test]
1695    fn round_trip_serialize_v1_family_person() {
1696        // Platform version that defaults to serialization v1
1697        let platform_version =
1698            PlatformVersion::get(9).unwrap_or_else(|_| PlatformVersion::latest());
1699
1700        // We need a non-V0 contract for v1 serialization. Use the latest platform version
1701        // to load the contract and create a document type.
1702        let contract = json_document_to_contract(
1703            "../rs-drive/tests/supporting_files/contract/family/family-contract.json",
1704            false,
1705            platform_version,
1706        )
1707        .expect("expected to load family contract");
1708
1709        let document_type = contract
1710            .document_type_for_name("person")
1711            .expect("expected person document type");
1712
1713        // Only test if we can actually produce v1 serialization
1714        // (contract must not be V0 and config must not be V0 for v1)
1715        if matches!(&contract, DataContract::V0(_)) {
1716            // V0 contracts always force serialize_v0, so we test that path instead
1717            let document = document_type
1718                .random_document(Some(99), platform_version)
1719                .expect("expected random document");
1720            let doc_v0 = match &document {
1721                crate::document::Document::V0(d) => d,
1722            };
1723            let serialized = doc_v0
1724                .serialize(document_type, &contract, platform_version)
1725                .expect("serialize should succeed");
1726            let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1727                .expect("from_bytes should succeed");
1728            assert_eq!(*doc_v0, deserialized);
1729        } else {
1730            let document = document_type
1731                .random_document(Some(99), platform_version)
1732                .expect("expected random document");
1733            let doc_v0 = match &document {
1734                crate::document::Document::V0(d) => d,
1735            };
1736            let serialized = doc_v0
1737                .serialize(document_type, &contract, platform_version)
1738                .expect("serialize should succeed");
1739            let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1740                .expect("from_bytes should succeed");
1741            assert_eq!(*doc_v0, deserialized);
1742        }
1743    }
1744
1745    #[test]
1746    fn round_trip_serialize_v2_latest_platform() {
1747        let platform_version = PlatformVersion::latest();
1748        let contract = json_document_to_contract(
1749            "../rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json",
1750            false,
1751            platform_version,
1752        )
1753        .expect("expected to load dashpay contract");
1754
1755        let document_type = contract
1756            .document_type_for_name("contactRequest")
1757            .expect("expected contactRequest document type");
1758
1759        let document = document_type
1760            .random_document(Some(7), platform_version)
1761            .expect("expected random document");
1762        let doc_v0 = match &document {
1763            crate::document::Document::V0(d) => d,
1764        };
1765        let serialized = doc_v0
1766            .serialize(document_type, &contract, platform_version)
1767            .expect("serialize should succeed");
1768        let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1769            .expect("from_bytes should succeed");
1770        assert_eq!(*doc_v0, deserialized);
1771    }
1772
1773    #[test]
1774    fn round_trip_withdrawals_document() {
1775        let platform_version = PlatformVersion::latest();
1776        let contract = withdrawals_contract(platform_version);
1777        let document_type = contract
1778            .document_type_for_name("withdrawal")
1779            .expect("expected withdrawal document type");
1780
1781        let document = document_type
1782            .random_document(Some(55), platform_version)
1783            .expect("expected random document");
1784        let doc_v0 = match &document {
1785            crate::document::Document::V0(d) => d,
1786        };
1787        let serialized = doc_v0
1788            .serialize(document_type, &contract, platform_version)
1789            .expect("serialize should succeed");
1790        let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1791            .expect("from_bytes should succeed");
1792        assert_eq!(*doc_v0, deserialized);
1793    }
1794
1795    // ================================================================
1796    //  serialize_specific_version tests
1797    // ================================================================
1798
1799    #[test]
1800    fn serialize_specific_version_v0_produces_version_0_prefix() {
1801        let platform_version = PlatformVersion::first();
1802        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1803        let document_type = contract
1804            .document_type_for_name(&type_name)
1805            .expect("expected document type");
1806
1807        let document = document_type
1808            .random_document(Some(1), platform_version)
1809            .expect("expected random document");
1810        let doc_v0 = match &document {
1811            crate::document::Document::V0(d) => d,
1812        };
1813
1814        let serialized = doc_v0
1815            .serialize_specific_version(document_type, &contract, 0)
1816            .expect("serialize_specific_version v0 should succeed");
1817
1818        // The first bytes should be varint-encoded 0
1819        let (version, _) = u64::decode_var(&serialized).expect("expected varint");
1820        assert_eq!(version, 0, "serialization version prefix should be 0");
1821    }
1822
1823    #[test]
1824    fn serialize_specific_version_rejects_v1_for_v0_contract() {
1825        let platform_version = PlatformVersion::first();
1826        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1827
1828        // V0 contracts should reject non-0 feature versions
1829        if matches!(&contract, DataContract::V0(_)) {
1830            let document_type = contract
1831                .document_type_for_name(&type_name)
1832                .expect("expected document type");
1833
1834            let document = document_type
1835                .random_document(Some(1), platform_version)
1836                .expect("expected random document");
1837            let doc_v0 = match &document {
1838                crate::document::Document::V0(d) => d,
1839            };
1840
1841            let result = doc_v0.serialize_specific_version(document_type, &contract, 1);
1842            assert!(
1843                result.is_err(),
1844                "V0 contract should reject serialize_specific_version with feature_version != 0"
1845            );
1846        }
1847    }
1848
1849    #[test]
1850    fn serialize_specific_version_unknown_version_returns_error() {
1851        let platform_version = PlatformVersion::latest();
1852        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1853        let document_type = contract
1854            .document_type_for_name(&type_name)
1855            .expect("expected document type");
1856
1857        let document = document_type
1858            .random_document(Some(1), platform_version)
1859            .expect("expected random document");
1860        let doc_v0 = match &document {
1861            crate::document::Document::V0(d) => d,
1862        };
1863
1864        let result = doc_v0.serialize_specific_version(document_type, &contract, 255);
1865        assert!(
1866            result.is_err(),
1867            "unknown feature version should produce an error"
1868        );
1869        // V0 contracts reject any non-0 version with NotSupported before reaching the
1870        // version dispatch. Non-V0 contracts would reach the version dispatch and return
1871        // UnknownVersionMismatch.
1872        match result.unwrap_err() {
1873            ProtocolError::UnknownVersionMismatch { received, .. } => {
1874                assert_eq!(received, 255);
1875            }
1876            ProtocolError::NotSupported(_) => {
1877                // V0 contract path: rejects non-0 feature version before dispatching
1878            }
1879            other => panic!(
1880                "expected UnknownVersionMismatch or NotSupported, got {:?}",
1881                other
1882            ),
1883        }
1884    }
1885
1886    // ================================================================
1887    //  from_bytes deserialization error cases
1888    // ================================================================
1889
1890    #[test]
1891    fn from_bytes_v0_rejects_too_small_buffer() {
1892        let platform_version = PlatformVersion::first();
1893        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1894        let document_type = contract
1895            .document_type_for_name(&type_name)
1896            .expect("expected document type");
1897
1898        // Buffer with varint(0) prefix then only 10 bytes (too small for id+owner_id = 64 bytes)
1899        let mut small_buf = 0u64.encode_var_vec();
1900        small_buf.extend_from_slice(&[0u8; 10]);
1901
1902        let result = DocumentV0::from_bytes(&small_buf, document_type, platform_version);
1903        assert!(
1904            result.is_err(),
1905            "buffer shorter than 64 bytes after version prefix should fail"
1906        );
1907    }
1908
1909    #[test]
1910    fn from_bytes_empty_buffer_fails() {
1911        let platform_version = PlatformVersion::first();
1912        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1913        let document_type = contract
1914            .document_type_for_name(&type_name)
1915            .expect("expected document type");
1916
1917        let result = DocumentV0::from_bytes(&[], document_type, platform_version);
1918        assert!(result.is_err(), "empty buffer should fail deserialization");
1919    }
1920
1921    #[test]
1922    fn from_bytes_unknown_serialization_version_fails() {
1923        let platform_version = PlatformVersion::first();
1924        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1925        let document_type = contract
1926            .document_type_for_name(&type_name)
1927            .expect("expected document type");
1928
1929        // Encode a version that is not 0, 1, or 2
1930        let mut buf = 200u64.encode_var_vec();
1931        buf.extend_from_slice(&[0u8; 100]); // padding
1932
1933        let result = DocumentV0::from_bytes(&buf, document_type, platform_version);
1934        assert!(result.is_err(), "unknown version should be rejected");
1935        match result.unwrap_err() {
1936            ProtocolError::UnknownVersionMismatch { received, .. } => {
1937                assert_eq!(received, 200);
1938            }
1939            other => panic!("expected UnknownVersionMismatch, got {:?}", other),
1940        }
1941    }
1942
1943    #[test]
1944    fn from_bytes_truncated_after_ids_fails() {
1945        let platform_version = PlatformVersion::first();
1946        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1947        let document_type = contract
1948            .document_type_for_name(&type_name)
1949            .expect("expected document type");
1950
1951        // Valid version prefix, valid 64-byte id+owner_id, then no more data
1952        // This should fail when trying to read revision or timestamp flags
1953        let mut buf = 0u64.encode_var_vec();
1954        buf.extend_from_slice(&[0xAA; 64]); // id (32) + owner_id (32)
1955
1956        let result = DocumentV0::from_bytes(&buf, document_type, platform_version);
1957        assert!(
1958            result.is_err(),
1959            "truncated buffer after ids should fail deserialization"
1960        );
1961    }
1962
1963    // ================================================================
1964    //  Serialization format: verify version prefix encoding
1965    // ================================================================
1966
1967    #[test]
1968    fn serialization_starts_with_correct_version_varint() {
1969        let platform_version = PlatformVersion::first();
1970        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1971        let document_type = contract
1972            .document_type_for_name(&type_name)
1973            .expect("expected document type");
1974
1975        let document = document_type
1976            .random_document(Some(100), platform_version)
1977            .expect("expected random document");
1978        let doc_v0 = match &document {
1979            crate::document::Document::V0(d) => d,
1980        };
1981
1982        // serialize_v0 should prefix with varint 0
1983        let bytes = doc_v0
1984            .serialize_v0(document_type)
1985            .expect("serialize_v0 should succeed");
1986        let (ver, _) = u64::decode_var(&bytes).expect("varint decode");
1987        assert_eq!(ver, 0);
1988
1989        // serialize_v1 should prefix with varint 1
1990        let bytes = doc_v0
1991            .serialize_v1(document_type)
1992            .expect("serialize_v1 should succeed");
1993        let (ver, _) = u64::decode_var(&bytes).expect("varint decode");
1994        assert_eq!(ver, 1);
1995
1996        // serialize_v2 should prefix with varint 2
1997        let bytes = doc_v0
1998            .serialize_v2(document_type)
1999            .expect("serialize_v2 should succeed");
2000        let (ver, _) = u64::decode_var(&bytes).expect("varint decode");
2001        assert_eq!(ver, 2);
2002    }
2003
2004    #[test]
2005    fn serialized_id_and_owner_id_are_embedded_after_version() {
2006        let platform_version = PlatformVersion::first();
2007        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2008        let document_type = contract
2009            .document_type_for_name(&type_name)
2010            .expect("expected document type");
2011
2012        let document = document_type
2013            .random_document(Some(42), platform_version)
2014            .expect("expected random document");
2015        let doc_v0 = match &document {
2016            crate::document::Document::V0(d) => d,
2017        };
2018
2019        let bytes = doc_v0
2020            .serialize_v0(document_type)
2021            .expect("serialize should succeed");
2022
2023        // Version 0 is a single-byte varint
2024        let (_, varint_len) = u64::decode_var(&bytes).expect("varint decode");
2025        let after_version = &bytes[varint_len..];
2026
2027        // Next 32 bytes = id
2028        assert_eq!(
2029            &after_version[..32],
2030            doc_v0.id.as_slice(),
2031            "id should be at offset after version"
2032        );
2033        // Following 32 bytes = owner_id
2034        assert_eq!(
2035            &after_version[32..64],
2036            doc_v0.owner_id.as_slice(),
2037            "owner_id should follow id"
2038        );
2039    }
2040
2041    // ================================================================
2042    //  Determinism: same document serializes to the same bytes
2043    // ================================================================
2044
2045    #[test]
2046    fn serialization_is_deterministic() {
2047        let platform_version = PlatformVersion::first();
2048        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2049        let document_type = contract
2050            .document_type_for_name(&type_name)
2051            .expect("expected document type");
2052
2053        let document = document_type
2054            .random_document(Some(99), platform_version)
2055            .expect("expected random document");
2056        let doc_v0 = match &document {
2057            crate::document::Document::V0(d) => d,
2058        };
2059
2060        let bytes1 = doc_v0
2061            .serialize(document_type, &contract, platform_version)
2062            .expect("first serialize");
2063        let bytes2 = doc_v0
2064            .serialize(document_type, &contract, platform_version)
2065            .expect("second serialize");
2066        assert_eq!(bytes1, bytes2, "serialization must be deterministic");
2067    }
2068
2069    // ================================================================
2070    //  Multiple random documents round-trip (fuzz-like)
2071    // ================================================================
2072
2073    #[test]
2074    fn round_trip_many_random_documents() {
2075        let platform_version = PlatformVersion::first();
2076        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2077        let document_type = contract
2078            .document_type_for_name(&type_name)
2079            .expect("expected document type");
2080
2081        for seed in 0..50u64 {
2082            let document = document_type
2083                .random_document(Some(seed), platform_version)
2084                .expect("expected random document");
2085            let doc_v0 = match &document {
2086                crate::document::Document::V0(d) => d,
2087            };
2088            let serialized = doc_v0
2089                .serialize(document_type, &contract, platform_version)
2090                .expect("serialize should succeed");
2091            let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
2092                .expect("from_bytes should succeed");
2093            assert_eq!(*doc_v0, deserialized, "round-trip mismatch for seed {seed}");
2094        }
2095    }
2096
2097    // ================================================================
2098    //  from_bytes_in_consensus
2099    // ================================================================
2100
2101    #[cfg(feature = "validation")]
2102    #[test]
2103    fn from_bytes_in_consensus_valid_data_returns_valid_result() {
2104        let platform_version = PlatformVersion::first();
2105        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2106        let document_type = contract
2107            .document_type_for_name(&type_name)
2108            .expect("expected document type");
2109
2110        let document = document_type
2111            .random_document(Some(77), platform_version)
2112            .expect("expected random document");
2113        let doc_v0 = match &document {
2114            crate::document::Document::V0(d) => d,
2115        };
2116        let serialized = doc_v0
2117            .serialize(document_type, &contract, platform_version)
2118            .expect("serialize should succeed");
2119
2120        let result =
2121            DocumentV0::from_bytes_in_consensus(&serialized, document_type, platform_version)
2122                .expect("from_bytes_in_consensus should not return ProtocolError");
2123
2124        assert!(result.is_valid(), "consensus result should be valid");
2125        let deserialized = result.into_data().expect("should have data");
2126        assert_eq!(*doc_v0, deserialized);
2127    }
2128
2129    #[cfg(feature = "validation")]
2130    #[test]
2131    fn from_bytes_in_consensus_invalid_data_returns_consensus_error() {
2132        let platform_version = PlatformVersion::first();
2133        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2134        let document_type = contract
2135            .document_type_for_name(&type_name)
2136            .expect("expected document type");
2137
2138        // Version 0, then truncated data
2139        let mut buf = 0u64.encode_var_vec();
2140        buf.extend_from_slice(&[0u8; 10]);
2141
2142        let result = DocumentV0::from_bytes_in_consensus(&buf, document_type, platform_version)
2143            .expect("should not return ProtocolError for consensus-level decode");
2144
2145        assert!(
2146            !result.is_valid(),
2147            "consensus result should contain errors for malformed data"
2148        );
2149    }
2150
2151    #[cfg(feature = "validation")]
2152    #[test]
2153    fn from_bytes_in_consensus_unknown_version_returns_protocol_error() {
2154        let platform_version = PlatformVersion::first();
2155        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2156        let document_type = contract
2157            .document_type_for_name(&type_name)
2158            .expect("expected document type");
2159
2160        let mut buf = 200u64.encode_var_vec();
2161        buf.extend_from_slice(&[0u8; 100]);
2162
2163        let result = DocumentV0::from_bytes_in_consensus(&buf, document_type, platform_version);
2164        assert!(
2165            result.is_err(),
2166            "unknown version should produce a ProtocolError, not a consensus error"
2167        );
2168    }
2169
2170    // ================================================================
2171    //  Known-bytes deserialization (golden test)
2172    // ================================================================
2173
2174    #[test]
2175    fn deserialize_known_withdrawal_bytes() {
2176        let platform_version = PlatformVersion::latest();
2177        let contract = withdrawals_contract(platform_version);
2178
2179        let document_type = contract
2180            .document_type_for_name("withdrawal")
2181            .expect("expected withdrawal document type");
2182
2183        // This is a real serialized withdrawal document (from existing test)
2184        let serialized = hex::decode(
2185            "010053626cafc76f47062f936c5938190f5f30aac997b8fc22e81c1d9a7f903bd9\
2186             fa8696d3f39c518784e53be79ee199e70387f9a7408254de920c1f3779de285601\
2187             00030000019782b96d140000019782b96d14000000000002540be40000000001\
2188             001976a9149e3292d2612122d81613fdb893dd36a04df3355588ac00",
2189        )
2190        .expect("expected valid hex");
2191
2192        let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
2193            .expect("expected deserialization to succeed");
2194
2195        // Verify known fields
2196        assert_eq!(
2197            hex::encode(deserialized.id.as_slice()),
2198            "0053626cafc76f47062f936c5938190f5f30aac997b8fc22e81c1d9a7f903bd9"
2199        );
2200        assert_eq!(
2201            hex::encode(deserialized.owner_id.as_slice()),
2202            "fa8696d3f39c518784e53be79ee199e70387f9a7408254de920c1f3779de2856"
2203        );
2204        assert_eq!(deserialized.revision, Some(1));
2205        assert_eq!(deserialized.created_at, Some(1750244879636));
2206        assert_eq!(deserialized.updated_at, Some(1750244879636));
2207    }
2208}