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 fn serialize_v0(&self, document_type: DocumentTypeRef) -> Result<Vec<u8>, ProtocolError> {
49 let mut buffer: Vec<u8> = 0u64.encode_var_vec(); buffer.extend(self.id.as_slice());
53
54 buffer.extend(self.owner_id.as_slice());
56
57 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 if let Some(created_at) = &self.created_at {
70 bitwise_exists_flag |= 1;
71 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 if let Some(updated_at) = &self.updated_at {
83 bitwise_exists_flag |= 2;
84 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 if let Some(transferred_at) = &self.transferred_at {
96 bitwise_exists_flag |= 4;
97 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 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 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 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 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 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 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 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 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 buffer.push(0);
229 Ok(())
230 }
231 } else {
232 if !property.required || property.transient {
233 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 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 buffer.push(0);
259 Ok(())
260 }
261 })?;
262
263 Ok(buffer)
264 }
265
266 fn serialize_v1(&self, document_type: DocumentTypeRef) -> Result<Vec<u8>, ProtocolError> {
273 let mut buffer: Vec<u8> = 1u64.encode_var_vec(); buffer.extend(self.id.as_slice());
277
278 buffer.extend(self.owner_id.as_slice());
280
281 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 if let Some(created_at) = &self.created_at {
294 bitwise_exists_flag |= 1;
295 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 if let Some(updated_at) = &self.updated_at {
307 bitwise_exists_flag |= 2;
308 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 if let Some(transferred_at) = &self.transferred_at {
320 bitwise_exists_flag |= 4;
321 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 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 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 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 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 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 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 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 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 buffer.push(0);
453 Ok(())
454 }
455 } else {
456 if !property.required || property.transient {
457 buffer.push(1);
459 }
460 let value = property
461 .property_type
462 .encode_value_ref_with_size(value, property.required)?;
463 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 buffer.push(0);
477 Ok(())
478 }
479 })?;
480
481 Ok(buffer)
482 }
483
484 fn serialize_v2(&self, document_type: DocumentTypeRef) -> Result<Vec<u8>, ProtocolError> {
490 let mut buffer: Vec<u8> = 2u64.encode_var_vec(); buffer.extend(self.id.as_slice());
494
495 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 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 if let Some(created_at) = &self.created_at {
522 bitwise_exists_flag |= 1;
523 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 if let Some(updated_at) = &self.updated_at {
535 bitwise_exists_flag |= 2;
536 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 if let Some(transferred_at) = &self.transferred_at {
548 bitwise_exists_flag |= 4;
549 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 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 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 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 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 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 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 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 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 buffer.push(0);
681 Ok(())
682 }
683 } else {
684 if !property.required || property.transient {
685 buffer.push(1);
687 }
688 let value = property
689 .property_type
690 .encode_value_ref_with_size(value, property.required)?;
691 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 buffer.push(0);
705 Ok(())
706 }
707 })?;
708
709 Ok(buffer)
710 }
711}
712
713impl DocumentPlatformDeserializationMethodsV0 for DocumentV0 {
714 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 #[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 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 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 #[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 let platform_version =
1694 PlatformVersion::get(9).unwrap_or_else(|_| PlatformVersion::latest());
1695
1696 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 if matches!(&contract, DataContract::V0(_)) {
1712 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 #[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 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 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 match result.unwrap_err() {
1855 ProtocolError::UnknownVersionMismatch { received, .. } => {
1856 assert_eq!(received, 255);
1857 }
1858 ProtocolError::NotSupported(_) => {
1859 }
1861 other => panic!(
1862 "expected UnknownVersionMismatch or NotSupported, got {:?}",
1863 other
1864 ),
1865 }
1866 }
1867
1868 #[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 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 let mut buf = 200u64.encode_var_vec();
1913 buf.extend_from_slice(&[0u8; 100]); 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 let mut buf = 0u64.encode_var_vec();
1936 buf.extend_from_slice(&[0xAA; 64]); 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 #[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 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 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 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 let (_, varint_len) = u64::decode_var(&bytes).expect("varint decode");
2003 let after_version = &bytes[varint_len..];
2004
2005 assert_eq!(
2007 &after_version[..32],
2008 doc_v0.id.as_slice(),
2009 "id should be at offset after version"
2010 );
2011 assert_eq!(
2013 &after_version[32..64],
2014 doc_v0.owner_id.as_slice(),
2015 "owner_id should follow id"
2016 );
2017 }
2018
2019 #[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 #[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 #[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 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 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 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 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 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 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 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 #[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 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 #[test]
2323 fn from_bytes_v2_non_transferable_type_does_not_persist_creator_id() {
2324 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 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 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 #[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 #[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 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 #[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 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 #[test]
2500 fn serialize_specific_version_rejects_v2_for_v0_contract() {
2501 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 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 #[test]
2541 fn from_bytes_v0_falls_back_to_v1_on_decoding_error() {
2542 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 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 let mut v1_bytes = doc_v0
2559 .serialize_v1(document_type)
2560 .expect("serialize_v1 should succeed");
2561 v1_bytes[0] = 0;
2563
2564 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 #[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 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 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}