Skip to main content

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 crate::document::Document::V0(doc_v0) = &document;
1657
1658        let serialized = doc_v0
1659            .serialize(document_type, &contract, platform_version)
1660            .expect("serialize should succeed");
1661
1662        let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1663            .expect("from_bytes should succeed");
1664
1665        assert_eq!(*doc_v0, deserialized);
1666    }
1667
1668    #[test]
1669    fn round_trip_serialize_v0_family_person() {
1670        let platform_version = PlatformVersion::first();
1671        let contract = family_contract(platform_version);
1672        let document_type = contract
1673            .document_type_for_name("person")
1674            .expect("expected person document type");
1675
1676        for seed in 0..20u64 {
1677            let document = document_type
1678                .random_document(Some(seed), platform_version)
1679                .expect("expected random document");
1680            let crate::document::Document::V0(doc_v0) = &document;
1681            let serialized = doc_v0
1682                .serialize(document_type, &contract, platform_version)
1683                .expect("serialize should succeed");
1684            let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1685                .expect("from_bytes should succeed");
1686            assert_eq!(*doc_v0, deserialized, "round-trip failed for seed {seed}");
1687        }
1688    }
1689
1690    #[test]
1691    fn round_trip_serialize_v1_family_person() {
1692        // Platform version that defaults to serialization v1
1693        let platform_version =
1694            PlatformVersion::get(9).unwrap_or_else(|_| PlatformVersion::latest());
1695
1696        // We need a non-V0 contract for v1 serialization. Use the latest platform version
1697        // to load the contract and create a document type.
1698        let contract = json_document_to_contract(
1699            "../rs-drive/tests/supporting_files/contract/family/family-contract.json",
1700            false,
1701            platform_version,
1702        )
1703        .expect("expected to load family contract");
1704
1705        let document_type = contract
1706            .document_type_for_name("person")
1707            .expect("expected person document type");
1708
1709        // Only test if we can actually produce v1 serialization
1710        // (contract must not be V0 and config must not be V0 for v1)
1711        if matches!(&contract, DataContract::V0(_)) {
1712            // V0 contracts always force serialize_v0, so we test that path instead
1713            let document = document_type
1714                .random_document(Some(99), platform_version)
1715                .expect("expected random document");
1716            let crate::document::Document::V0(doc_v0) = &document;
1717            let serialized = doc_v0
1718                .serialize(document_type, &contract, platform_version)
1719                .expect("serialize should succeed");
1720            let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1721                .expect("from_bytes should succeed");
1722            assert_eq!(*doc_v0, deserialized);
1723        } else {
1724            let document = document_type
1725                .random_document(Some(99), platform_version)
1726                .expect("expected random document");
1727            let crate::document::Document::V0(doc_v0) = &document;
1728            let serialized = doc_v0
1729                .serialize(document_type, &contract, platform_version)
1730                .expect("serialize should succeed");
1731            let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1732                .expect("from_bytes should succeed");
1733            assert_eq!(*doc_v0, deserialized);
1734        }
1735    }
1736
1737    #[test]
1738    fn round_trip_serialize_v2_latest_platform() {
1739        let platform_version = PlatformVersion::latest();
1740        let contract = json_document_to_contract(
1741            "../rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json",
1742            false,
1743            platform_version,
1744        )
1745        .expect("expected to load dashpay contract");
1746
1747        let document_type = contract
1748            .document_type_for_name("contactRequest")
1749            .expect("expected contactRequest document type");
1750
1751        let document = document_type
1752            .random_document(Some(7), platform_version)
1753            .expect("expected random document");
1754        let crate::document::Document::V0(doc_v0) = &document;
1755        let serialized = doc_v0
1756            .serialize(document_type, &contract, platform_version)
1757            .expect("serialize should succeed");
1758        let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1759            .expect("from_bytes should succeed");
1760        assert_eq!(*doc_v0, deserialized);
1761    }
1762
1763    #[test]
1764    fn round_trip_withdrawals_document() {
1765        let platform_version = PlatformVersion::latest();
1766        let contract = withdrawals_contract(platform_version);
1767        let document_type = contract
1768            .document_type_for_name("withdrawal")
1769            .expect("expected withdrawal document type");
1770
1771        let document = document_type
1772            .random_document(Some(55), platform_version)
1773            .expect("expected random document");
1774        let crate::document::Document::V0(doc_v0) = &document;
1775        let serialized = doc_v0
1776            .serialize(document_type, &contract, platform_version)
1777            .expect("serialize should succeed");
1778        let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
1779            .expect("from_bytes should succeed");
1780        assert_eq!(*doc_v0, deserialized);
1781    }
1782
1783    // ================================================================
1784    //  serialize_specific_version tests
1785    // ================================================================
1786
1787    #[test]
1788    fn serialize_specific_version_v0_produces_version_0_prefix() {
1789        let platform_version = PlatformVersion::first();
1790        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1791        let document_type = contract
1792            .document_type_for_name(&type_name)
1793            .expect("expected document type");
1794
1795        let document = document_type
1796            .random_document(Some(1), platform_version)
1797            .expect("expected random document");
1798        let crate::document::Document::V0(doc_v0) = &document;
1799
1800        let serialized = doc_v0
1801            .serialize_specific_version(document_type, &contract, 0)
1802            .expect("serialize_specific_version v0 should succeed");
1803
1804        // The first bytes should be varint-encoded 0
1805        let (version, _) = u64::decode_var(&serialized).expect("expected varint");
1806        assert_eq!(version, 0, "serialization version prefix should be 0");
1807    }
1808
1809    #[test]
1810    fn serialize_specific_version_rejects_v1_for_v0_contract() {
1811        let platform_version = PlatformVersion::first();
1812        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1813
1814        // V0 contracts should reject non-0 feature versions
1815        if matches!(&contract, DataContract::V0(_)) {
1816            let document_type = contract
1817                .document_type_for_name(&type_name)
1818                .expect("expected document type");
1819
1820            let document = document_type
1821                .random_document(Some(1), platform_version)
1822                .expect("expected random document");
1823            let crate::document::Document::V0(doc_v0) = &document;
1824
1825            let result = doc_v0.serialize_specific_version(document_type, &contract, 1);
1826            assert!(
1827                result.is_err(),
1828                "V0 contract should reject serialize_specific_version with feature_version != 0"
1829            );
1830        }
1831    }
1832
1833    #[test]
1834    fn serialize_specific_version_unknown_version_returns_error() {
1835        let platform_version = PlatformVersion::latest();
1836        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1837        let document_type = contract
1838            .document_type_for_name(&type_name)
1839            .expect("expected document type");
1840
1841        let document = document_type
1842            .random_document(Some(1), platform_version)
1843            .expect("expected random document");
1844        let crate::document::Document::V0(doc_v0) = &document;
1845
1846        let result = doc_v0.serialize_specific_version(document_type, &contract, 255);
1847        assert!(
1848            result.is_err(),
1849            "unknown feature version should produce an error"
1850        );
1851        // V0 contracts reject any non-0 version with NotSupported before reaching the
1852        // version dispatch. Non-V0 contracts would reach the version dispatch and return
1853        // UnknownVersionMismatch.
1854        match result.unwrap_err() {
1855            ProtocolError::UnknownVersionMismatch { received, .. } => {
1856                assert_eq!(received, 255);
1857            }
1858            ProtocolError::NotSupported(_) => {
1859                // V0 contract path: rejects non-0 feature version before dispatching
1860            }
1861            other => panic!(
1862                "expected UnknownVersionMismatch or NotSupported, got {:?}",
1863                other
1864            ),
1865        }
1866    }
1867
1868    // ================================================================
1869    //  from_bytes deserialization error cases
1870    // ================================================================
1871
1872    #[test]
1873    fn from_bytes_v0_rejects_too_small_buffer() {
1874        let platform_version = PlatformVersion::first();
1875        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1876        let document_type = contract
1877            .document_type_for_name(&type_name)
1878            .expect("expected document type");
1879
1880        // Buffer with varint(0) prefix then only 10 bytes (too small for id+owner_id = 64 bytes)
1881        let mut small_buf = 0u64.encode_var_vec();
1882        small_buf.extend_from_slice(&[0u8; 10]);
1883
1884        let result = DocumentV0::from_bytes(&small_buf, document_type, platform_version);
1885        assert!(
1886            result.is_err(),
1887            "buffer shorter than 64 bytes after version prefix should fail"
1888        );
1889    }
1890
1891    #[test]
1892    fn from_bytes_empty_buffer_fails() {
1893        let platform_version = PlatformVersion::first();
1894        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1895        let document_type = contract
1896            .document_type_for_name(&type_name)
1897            .expect("expected document type");
1898
1899        let result = DocumentV0::from_bytes(&[], document_type, platform_version);
1900        assert!(result.is_err(), "empty buffer should fail deserialization");
1901    }
1902
1903    #[test]
1904    fn from_bytes_unknown_serialization_version_fails() {
1905        let platform_version = PlatformVersion::first();
1906        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1907        let document_type = contract
1908            .document_type_for_name(&type_name)
1909            .expect("expected document type");
1910
1911        // Encode a version that is not 0, 1, or 2
1912        let mut buf = 200u64.encode_var_vec();
1913        buf.extend_from_slice(&[0u8; 100]); // padding
1914
1915        let result = DocumentV0::from_bytes(&buf, document_type, platform_version);
1916        assert!(result.is_err(), "unknown version should be rejected");
1917        match result.unwrap_err() {
1918            ProtocolError::UnknownVersionMismatch { received, .. } => {
1919                assert_eq!(received, 200);
1920            }
1921            other => panic!("expected UnknownVersionMismatch, got {:?}", other),
1922        }
1923    }
1924
1925    #[test]
1926    fn from_bytes_truncated_after_ids_fails() {
1927        let platform_version = PlatformVersion::first();
1928        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1929        let document_type = contract
1930            .document_type_for_name(&type_name)
1931            .expect("expected document type");
1932
1933        // Valid version prefix, valid 64-byte id+owner_id, then no more data
1934        // This should fail when trying to read revision or timestamp flags
1935        let mut buf = 0u64.encode_var_vec();
1936        buf.extend_from_slice(&[0xAA; 64]); // id (32) + owner_id (32)
1937
1938        let result = DocumentV0::from_bytes(&buf, document_type, platform_version);
1939        assert!(
1940            result.is_err(),
1941            "truncated buffer after ids should fail deserialization"
1942        );
1943    }
1944
1945    // ================================================================
1946    //  Serialization format: verify version prefix encoding
1947    // ================================================================
1948
1949    #[test]
1950    fn serialization_starts_with_correct_version_varint() {
1951        let platform_version = PlatformVersion::first();
1952        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1953        let document_type = contract
1954            .document_type_for_name(&type_name)
1955            .expect("expected document type");
1956
1957        let document = document_type
1958            .random_document(Some(100), platform_version)
1959            .expect("expected random document");
1960        let crate::document::Document::V0(doc_v0) = &document;
1961
1962        // serialize_v0 should prefix with varint 0
1963        let bytes = doc_v0
1964            .serialize_v0(document_type)
1965            .expect("serialize_v0 should succeed");
1966        let (ver, _) = u64::decode_var(&bytes).expect("varint decode");
1967        assert_eq!(ver, 0);
1968
1969        // serialize_v1 should prefix with varint 1
1970        let bytes = doc_v0
1971            .serialize_v1(document_type)
1972            .expect("serialize_v1 should succeed");
1973        let (ver, _) = u64::decode_var(&bytes).expect("varint decode");
1974        assert_eq!(ver, 1);
1975
1976        // serialize_v2 should prefix with varint 2
1977        let bytes = doc_v0
1978            .serialize_v2(document_type)
1979            .expect("serialize_v2 should succeed");
1980        let (ver, _) = u64::decode_var(&bytes).expect("varint decode");
1981        assert_eq!(ver, 2);
1982    }
1983
1984    #[test]
1985    fn serialized_id_and_owner_id_are_embedded_after_version() {
1986        let platform_version = PlatformVersion::first();
1987        let (contract, type_name) = dashpay_contract_and_type(platform_version);
1988        let document_type = contract
1989            .document_type_for_name(&type_name)
1990            .expect("expected document type");
1991
1992        let document = document_type
1993            .random_document(Some(42), platform_version)
1994            .expect("expected random document");
1995        let crate::document::Document::V0(doc_v0) = &document;
1996
1997        let bytes = doc_v0
1998            .serialize_v0(document_type)
1999            .expect("serialize should succeed");
2000
2001        // Version 0 is a single-byte varint
2002        let (_, varint_len) = u64::decode_var(&bytes).expect("varint decode");
2003        let after_version = &bytes[varint_len..];
2004
2005        // Next 32 bytes = id
2006        assert_eq!(
2007            &after_version[..32],
2008            doc_v0.id.as_slice(),
2009            "id should be at offset after version"
2010        );
2011        // Following 32 bytes = owner_id
2012        assert_eq!(
2013            &after_version[32..64],
2014            doc_v0.owner_id.as_slice(),
2015            "owner_id should follow id"
2016        );
2017    }
2018
2019    // ================================================================
2020    //  Determinism: same document serializes to the same bytes
2021    // ================================================================
2022
2023    #[test]
2024    fn serialization_is_deterministic() {
2025        let platform_version = PlatformVersion::first();
2026        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2027        let document_type = contract
2028            .document_type_for_name(&type_name)
2029            .expect("expected document type");
2030
2031        let document = document_type
2032            .random_document(Some(99), platform_version)
2033            .expect("expected random document");
2034        let crate::document::Document::V0(doc_v0) = &document;
2035
2036        let bytes1 = doc_v0
2037            .serialize(document_type, &contract, platform_version)
2038            .expect("first serialize");
2039        let bytes2 = doc_v0
2040            .serialize(document_type, &contract, platform_version)
2041            .expect("second serialize");
2042        assert_eq!(bytes1, bytes2, "serialization must be deterministic");
2043    }
2044
2045    // ================================================================
2046    //  Multiple random documents round-trip (fuzz-like)
2047    // ================================================================
2048
2049    #[test]
2050    fn round_trip_many_random_documents() {
2051        let platform_version = PlatformVersion::first();
2052        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2053        let document_type = contract
2054            .document_type_for_name(&type_name)
2055            .expect("expected document type");
2056
2057        for seed in 0..50u64 {
2058            let document = document_type
2059                .random_document(Some(seed), platform_version)
2060                .expect("expected random document");
2061            let crate::document::Document::V0(doc_v0) = &document;
2062            let serialized = doc_v0
2063                .serialize(document_type, &contract, platform_version)
2064                .expect("serialize should succeed");
2065            let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
2066                .expect("from_bytes should succeed");
2067            assert_eq!(*doc_v0, deserialized, "round-trip mismatch for seed {seed}");
2068        }
2069    }
2070
2071    // ================================================================
2072    //  from_bytes_in_consensus
2073    // ================================================================
2074
2075    #[cfg(feature = "validation")]
2076    #[test]
2077    fn from_bytes_in_consensus_valid_data_returns_valid_result() {
2078        let platform_version = PlatformVersion::first();
2079        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2080        let document_type = contract
2081            .document_type_for_name(&type_name)
2082            .expect("expected document type");
2083
2084        let document = document_type
2085            .random_document(Some(77), platform_version)
2086            .expect("expected random document");
2087        let crate::document::Document::V0(doc_v0) = &document;
2088        let serialized = doc_v0
2089            .serialize(document_type, &contract, platform_version)
2090            .expect("serialize should succeed");
2091
2092        let result =
2093            DocumentV0::from_bytes_in_consensus(&serialized, document_type, platform_version)
2094                .expect("from_bytes_in_consensus should not return ProtocolError");
2095
2096        assert!(result.is_valid(), "consensus result should be valid");
2097        let deserialized = result.into_data().expect("should have data");
2098        assert_eq!(*doc_v0, deserialized);
2099    }
2100
2101    #[cfg(feature = "validation")]
2102    #[test]
2103    fn from_bytes_in_consensus_invalid_data_returns_consensus_error() {
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        // Version 0, then truncated data
2111        let mut buf = 0u64.encode_var_vec();
2112        buf.extend_from_slice(&[0u8; 10]);
2113
2114        let result = DocumentV0::from_bytes_in_consensus(&buf, document_type, platform_version)
2115            .expect("should not return ProtocolError for consensus-level decode");
2116
2117        assert!(
2118            !result.is_valid(),
2119            "consensus result should contain errors for malformed data"
2120        );
2121    }
2122
2123    #[cfg(feature = "validation")]
2124    #[test]
2125    fn from_bytes_in_consensus_unknown_version_returns_protocol_error() {
2126        let platform_version = PlatformVersion::first();
2127        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2128        let document_type = contract
2129            .document_type_for_name(&type_name)
2130            .expect("expected document type");
2131
2132        let mut buf = 200u64.encode_var_vec();
2133        buf.extend_from_slice(&[0u8; 100]);
2134
2135        let result = DocumentV0::from_bytes_in_consensus(&buf, document_type, platform_version);
2136        assert!(
2137            result.is_err(),
2138            "unknown version should produce a ProtocolError, not a consensus error"
2139        );
2140    }
2141
2142    // ================================================================
2143    //  Missing-required-field errors in serialize_v0 / v1 / v2.
2144    //  The withdrawal contract requires both $createdAt and $updatedAt,
2145    //  so a DocumentV0 lacking those should fail serialization.
2146    // ================================================================
2147
2148    fn doc_with_ids() -> DocumentV0 {
2149        DocumentV0 {
2150            id: Identifier::new([1u8; 32]),
2151            owner_id: Identifier::new([2u8; 32]),
2152            properties: BTreeMap::new(),
2153            revision: Some(1),
2154            created_at: None,
2155            updated_at: None,
2156            transferred_at: None,
2157            created_at_block_height: None,
2158            updated_at_block_height: None,
2159            transferred_at_block_height: None,
2160            created_at_core_block_height: None,
2161            updated_at_core_block_height: None,
2162            transferred_at_core_block_height: None,
2163            creator_id: None,
2164        }
2165    }
2166
2167    #[test]
2168    fn serialize_v0_missing_created_at_errors_when_required() {
2169        let platform_version = PlatformVersion::latest();
2170        let contract = withdrawals_contract(platform_version);
2171        let document_type = contract
2172            .document_type_for_name("withdrawal")
2173            .expect("withdrawal document type");
2174
2175        // Build a document missing $createdAt. Don't include any user-defined
2176        // required properties either — we want to trigger the $createdAt path
2177        // before the user-property path.
2178        let doc = doc_with_ids();
2179
2180        let err = doc
2181            .serialize_v0(document_type)
2182            .expect_err("serialize_v0 should fail for missing $createdAt");
2183        match err {
2184            ProtocolError::DataContractError(DataContractError::MissingRequiredKey(msg)) => {
2185                assert!(
2186                    msg.contains("created at"),
2187                    "expected missing-created-at message, got: {msg}"
2188                );
2189            }
2190            other => panic!(
2191                "expected MissingRequiredKey for created_at, got {:?}",
2192                other
2193            ),
2194        }
2195    }
2196
2197    #[test]
2198    fn serialize_v0_missing_updated_at_errors_when_required() {
2199        let platform_version = PlatformVersion::latest();
2200        let contract = withdrawals_contract(platform_version);
2201        let document_type = contract
2202            .document_type_for_name("withdrawal")
2203            .expect("withdrawal document type");
2204
2205        // Supply $createdAt but not $updatedAt — both are required.
2206        let mut doc = doc_with_ids();
2207        doc.created_at = Some(1_700_000_000_000);
2208
2209        let err = doc
2210            .serialize_v0(document_type)
2211            .expect_err("serialize_v0 should fail for missing $updatedAt");
2212        match err {
2213            ProtocolError::DataContractError(DataContractError::MissingRequiredKey(msg)) => {
2214                assert!(
2215                    msg.contains("updated at"),
2216                    "expected missing-updated-at message, got: {msg}"
2217                );
2218            }
2219            other => panic!(
2220                "expected MissingRequiredKey for updated_at, got {:?}",
2221                other
2222            ),
2223        }
2224    }
2225
2226    #[test]
2227    fn serialize_v1_missing_created_at_errors_when_required() {
2228        let platform_version = PlatformVersion::latest();
2229        let contract = withdrawals_contract(platform_version);
2230        let document_type = contract
2231            .document_type_for_name("withdrawal")
2232            .expect("withdrawal document type");
2233
2234        let doc = doc_with_ids();
2235        let err = doc
2236            .serialize_v1(document_type)
2237            .expect_err("serialize_v1 should fail for missing $createdAt");
2238        assert!(matches!(
2239            err,
2240            ProtocolError::DataContractError(DataContractError::MissingRequiredKey(_))
2241        ));
2242    }
2243
2244    #[test]
2245    fn serialize_v2_missing_created_at_errors_when_required() {
2246        let platform_version = PlatformVersion::latest();
2247        let contract = withdrawals_contract(platform_version);
2248        let document_type = contract
2249            .document_type_for_name("withdrawal")
2250            .expect("withdrawal document type");
2251
2252        let doc = doc_with_ids();
2253        let err = doc
2254            .serialize_v2(document_type)
2255            .expect_err("serialize_v2 should fail for missing $createdAt");
2256        assert!(matches!(
2257            err,
2258            ProtocolError::DataContractError(DataContractError::MissingRequiredKey(_))
2259        ));
2260    }
2261
2262    #[test]
2263    fn serialize_v0_missing_required_user_property_errors() {
2264        // Family `person` requires `firstName`, `lastName`, `age`.
2265        let platform_version = PlatformVersion::first();
2266        let contract = family_contract(platform_version);
2267        let document_type = contract
2268            .document_type_for_name("person")
2269            .expect("person document type");
2270
2271        // Document with only ids, no user-defined required properties set.
2272        let doc = doc_with_ids();
2273
2274        let err = doc
2275            .serialize_v0(document_type)
2276            .expect_err("serialize_v0 should fail for missing required property");
2277        match err {
2278            ProtocolError::DataContractError(DataContractError::MissingRequiredKey(msg)) => {
2279                // The error message includes the field name for user-defined required fields.
2280                let any_expected = msg.contains("firstName")
2281                    || msg.contains("lastName")
2282                    || msg.contains("age")
2283                    || msg.contains("required field");
2284                assert!(any_expected, "unexpected error message: {msg}");
2285            }
2286            other => panic!("expected MissingRequiredKey, got {:?}", other),
2287        }
2288    }
2289
2290    // ================================================================
2291    //  from_bytes: V1 prefix dispatches to from_bytes_v1 directly
2292    // ================================================================
2293
2294    #[test]
2295    fn from_bytes_v1_prefix_dispatches_to_v1_path() {
2296        let platform_version = PlatformVersion::latest();
2297        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2298        let document_type = contract
2299            .document_type_for_name(&type_name)
2300            .expect("expected document type");
2301
2302        let document = document_type
2303            .random_document(Some(123), platform_version)
2304            .expect("expected random document");
2305        let crate::document::Document::V0(doc_v0) = &document;
2306
2307        // Bypass the V0-contract gate by calling serialize_v1 directly: the
2308        // resulting varint-1 prefix must round-trip through from_bytes.
2309        let bytes = doc_v0.serialize_v1(document_type).expect("serialize_v1");
2310        let (ver, _) = u64::decode_var(&bytes).expect("varint");
2311        assert_eq!(ver, 1);
2312        let recovered = DocumentV0::from_bytes(&bytes, document_type, platform_version)
2313            .expect("from_bytes should dispatch to v1");
2314        assert_eq!(*doc_v0, recovered);
2315    }
2316
2317    // ================================================================
2318    //  from_bytes: V2 prefix round-trip for documents with a creator_id
2319    //  (contactRequest is transferable, so v2 records the creator flag).
2320    // ================================================================
2321
2322    #[test]
2323    fn from_bytes_v2_non_transferable_type_does_not_persist_creator_id() {
2324        // frozen: V0 consensus behavior — contactRequest is non-transferable
2325        // with TradeMode::None, so v2 intentionally skips the creator_id byte
2326        // in both serialize_v2 and from_bytes_v2. Assigning a creator_id on
2327        // the source document is therefore NOT round-tripped.
2328        let platform_version = PlatformVersion::latest();
2329        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2330        let document_type = contract
2331            .document_type_for_name(&type_name)
2332            .expect("expected document type");
2333
2334        let document = document_type
2335            .random_document(Some(321), platform_version)
2336            .expect("expected random document");
2337        let crate::document::Document::V0(mut doc_v0) = document;
2338        // Even setting a creator_id here has no on-wire effect for this type.
2339        doc_v0.creator_id = Some(Identifier::new([0xAB; 32]));
2340
2341        let bytes = doc_v0.serialize_v2(document_type).expect("serialize_v2");
2342        let (ver, _) = u64::decode_var(&bytes).expect("varint");
2343        assert_eq!(ver, 2);
2344
2345        let recovered = DocumentV0::from_bytes(&bytes, document_type, platform_version)
2346            .expect("from_bytes should dispatch to v2");
2347        assert_eq!(doc_v0.id, recovered.id);
2348        assert_eq!(doc_v0.owner_id, recovered.owner_id);
2349        assert_eq!(
2350            recovered.creator_id, None,
2351            "creator_id is not encoded for non-transferable / TradeMode::None types"
2352        );
2353    }
2354
2355    #[test]
2356    fn from_bytes_v2_prefix_round_trip_with_none_creator_id() {
2357        let platform_version = PlatformVersion::latest();
2358        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2359        let document_type = contract
2360            .document_type_for_name(&type_name)
2361            .expect("expected document type");
2362
2363        let document = document_type
2364            .random_document(Some(999), platform_version)
2365            .expect("expected random document");
2366        let crate::document::Document::V0(mut doc_v0) = document;
2367        // creator_id is None — exercise the else-branch of v2's creator check.
2368        doc_v0.creator_id = None;
2369
2370        let bytes = doc_v0.serialize_v2(document_type).expect("serialize_v2");
2371        let recovered =
2372            DocumentV0::from_bytes(&bytes, document_type, platform_version).expect("from_bytes v2");
2373        assert_eq!(recovered.creator_id, None);
2374    }
2375
2376    // ================================================================
2377    //  from_bytes_v1 / v2 directly — too-small buffers should error
2378    //  before we read any id / owner id bytes.
2379    // ================================================================
2380
2381    #[test]
2382    fn from_bytes_v1_direct_too_small_buffer_errors() {
2383        let platform_version = PlatformVersion::first();
2384        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2385        let document_type = contract
2386            .document_type_for_name(&type_name)
2387            .expect("expected document type");
2388
2389        let result = DocumentV0::from_bytes_v1(&[0u8; 10], document_type, platform_version);
2390        assert!(
2391            result.is_err(),
2392            "from_bytes_v1 should fail for buffer < 64 bytes"
2393        );
2394    }
2395
2396    #[test]
2397    fn from_bytes_v2_direct_too_small_buffer_errors() {
2398        let platform_version = PlatformVersion::first();
2399        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2400        let document_type = contract
2401            .document_type_for_name(&type_name)
2402            .expect("expected document type");
2403
2404        let result = DocumentV0::from_bytes_v2(&[0u8; 10], document_type, platform_version);
2405        assert!(
2406            result.is_err(),
2407            "from_bytes_v2 should fail for buffer < 64 bytes"
2408        );
2409    }
2410
2411    #[test]
2412    fn from_bytes_v0_direct_too_small_buffer_errors() {
2413        let platform_version = PlatformVersion::first();
2414        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2415        let document_type = contract
2416            .document_type_for_name(&type_name)
2417            .expect("expected document type");
2418
2419        let result = DocumentV0::from_bytes_v0(&[0u8; 10], document_type, platform_version);
2420        assert!(
2421            result.is_err(),
2422            "from_bytes_v0 should fail for buffer < 64 bytes"
2423        );
2424    }
2425
2426    // ================================================================
2427    //  from_bytes: V1 prefix with truncated post-id data errors
2428    // ================================================================
2429
2430    #[test]
2431    fn from_bytes_v1_truncated_post_ids_errors() {
2432        let platform_version = PlatformVersion::latest();
2433        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2434        let document_type = contract
2435            .document_type_for_name(&type_name)
2436            .expect("expected document type");
2437
2438        // V1 varint + 64 bytes (id + owner_id) — nothing after that, so the
2439        // revision / timestamp_flags read must fail.
2440        let mut buf = 1u64.encode_var_vec();
2441        buf.extend_from_slice(&[0xCD; 64]);
2442
2443        let result = DocumentV0::from_bytes(&buf, document_type, platform_version);
2444        assert!(
2445            result.is_err(),
2446            "v1 with truncated post-ids should fail deserialization"
2447        );
2448    }
2449
2450    #[test]
2451    fn from_bytes_v2_truncated_post_ids_errors() {
2452        let platform_version = PlatformVersion::latest();
2453        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2454        let document_type = contract
2455            .document_type_for_name(&type_name)
2456            .expect("expected document type");
2457
2458        let mut buf = 2u64.encode_var_vec();
2459        buf.extend_from_slice(&[0xCD; 64]);
2460
2461        let result = DocumentV0::from_bytes(&buf, document_type, platform_version);
2462        assert!(
2463            result.is_err(),
2464            "v2 with truncated post-ids should fail deserialization"
2465        );
2466    }
2467
2468    // ================================================================
2469    //  serialize_specific_version: V0 contract + feature_version 0
2470    //  should succeed (the V0-gated NotSupported branch is NOT hit).
2471    // ================================================================
2472
2473    #[test]
2474    fn serialize_specific_version_v0_contract_feature_version_0_succeeds() {
2475        let platform_version = PlatformVersion::first();
2476        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2477        let document_type = contract
2478            .document_type_for_name(&type_name)
2479            .expect("expected document type");
2480
2481        let document = document_type
2482            .random_document(Some(5), platform_version)
2483            .expect("expected random document");
2484        let crate::document::Document::V0(doc_v0) = &document;
2485
2486        // feature_version 0 is explicitly allowed for V0 contracts.
2487        let bytes = doc_v0
2488            .serialize_specific_version(document_type, &contract, 0)
2489            .expect("serialize_specific_version v0 should succeed on a V0 contract");
2490        let (ver, _) = u64::decode_var(&bytes).expect("varint decode");
2491        assert_eq!(ver, 0);
2492    }
2493
2494    // ================================================================
2495    //  serialize_specific_version: feature_version 2 with a non-V0
2496    //  contract (latest platform version) should succeed.
2497    // ================================================================
2498
2499    #[test]
2500    fn serialize_specific_version_rejects_v2_for_v0_contract() {
2501        // V0 contracts always force serialize_v0, so feature_version 2 is
2502        // rejected with NotSupported before reaching the version dispatch.
2503        // Use `PlatformVersion::first()` so the fixture is guaranteed to load
2504        // as a V0 contract — without this, the test could pass vacuously if
2505        // the dashpay contract began deserializing as a non-V0 variant.
2506        let platform_version = PlatformVersion::first();
2507        let (contract, type_name) = dashpay_contract_and_type(platform_version);
2508        let document_type = contract
2509            .document_type_for_name(&type_name)
2510            .expect("expected document type");
2511
2512        let document = document_type
2513            .random_document(Some(17), platform_version)
2514            .expect("expected random document");
2515        let crate::document::Document::V0(doc_v0) = &document;
2516
2517        // Precondition: the fixture must actually be a V0 contract, otherwise
2518        // the NotSupported branch we intend to exercise would never be hit.
2519        assert!(
2520            matches!(&contract, DataContract::V0(_)),
2521            "fixture must be a V0 contract to exercise the V0-gated NotSupported branch"
2522        );
2523
2524        let err = doc_v0
2525            .serialize_specific_version(document_type, &contract, 2)
2526            .expect_err("V0 contract should reject v2");
2527        match err {
2528            ProtocolError::NotSupported(_) => {}
2529            other => panic!("expected NotSupported, got {:?}", other),
2530        }
2531    }
2532
2533    // ================================================================
2534    //  from_bytes V0-then-V1 fallback: a valid V1 buffer with a V0
2535    //  varint prefix should still round-trip via the fallback path.
2536    //  Construct bytes by serializing v1 and then overwriting the
2537    //  varint prefix to 0.
2538    // ================================================================
2539
2540    #[test]
2541    fn from_bytes_v0_falls_back_to_v1_on_decoding_error() {
2542        // Use a contract whose properties are all integers so v0 (I64) and v1
2543        // (actual type) produce different encoded lengths / types. family's
2544        // `person` has one integer field `age`, suitable for fallback testing.
2545        let platform_version = PlatformVersion::latest();
2546        let contract = family_contract(platform_version);
2547        let document_type = contract
2548            .document_type_for_name("person")
2549            .expect("person document type");
2550
2551        // Random document serialized in v1 format (integers kept as native).
2552        let document = document_type
2553            .random_document(Some(55), platform_version)
2554            .expect("random document");
2555        let crate::document::Document::V0(doc_v0) = document;
2556
2557        // Serialize in v1 explicitly.
2558        let mut v1_bytes = doc_v0
2559            .serialize_v1(document_type)
2560            .expect("serialize_v1 should succeed");
2561        // Overwrite the varint-1 prefix with varint-0.
2562        v1_bytes[0] = 0;
2563
2564        // from_bytes dispatches to v0, which fails on the mismatched layout,
2565        // then retries via v1 — the fallback must recover the original document.
2566        let recovered = DocumentV0::from_bytes(&v1_bytes, document_type, platform_version)
2567            .expect("v0-prefixed v1 payload must fall back to v1 deserialization");
2568        assert_eq!(recovered, doc_v0);
2569    }
2570
2571    // ================================================================
2572    //  Known-bytes deserialization (golden test)
2573    // ================================================================
2574
2575    #[test]
2576    fn deserialize_known_withdrawal_bytes() {
2577        let platform_version = PlatformVersion::latest();
2578        let contract = withdrawals_contract(platform_version);
2579
2580        let document_type = contract
2581            .document_type_for_name("withdrawal")
2582            .expect("expected withdrawal document type");
2583
2584        // This is a real serialized withdrawal document (from existing test)
2585        let serialized = hex::decode(
2586            "010053626cafc76f47062f936c5938190f5f30aac997b8fc22e81c1d9a7f903bd9\
2587             fa8696d3f39c518784e53be79ee199e70387f9a7408254de920c1f3779de285601\
2588             00030000019782b96d140000019782b96d14000000000002540be40000000001\
2589             001976a9149e3292d2612122d81613fdb893dd36a04df3355588ac00",
2590        )
2591        .expect("expected valid hex");
2592
2593        let deserialized = DocumentV0::from_bytes(&serialized, document_type, platform_version)
2594            .expect("expected deserialization to succeed");
2595
2596        // Verify known fields
2597        assert_eq!(
2598            hex::encode(deserialized.id.as_slice()),
2599            "0053626cafc76f47062f936c5938190f5f30aac997b8fc22e81c1d9a7f903bd9"
2600        );
2601        assert_eq!(
2602            hex::encode(deserialized.owner_id.as_slice()),
2603            "fa8696d3f39c518784e53be79ee199e70387f9a7408254de920c1f3779de2856"
2604        );
2605        assert_eq!(deserialized.revision, Some(1));
2606        assert_eq!(deserialized.created_at, Some(1750244879636));
2607        assert_eq!(deserialized.updated_at, Some(1750244879636));
2608    }
2609}