1use super::EMPTY_KEYWORDS;
2use crate::data_contract::associated_token::token_configuration::TokenConfiguration;
3use crate::data_contract::config::DataContractConfig;
4use crate::data_contract::group::Group;
5use crate::data_contract::serialized_version::v0::DataContractInSerializationFormatV0;
6use crate::data_contract::serialized_version::v1::DataContractInSerializationFormatV1;
7use crate::data_contract::v0::DataContractV0;
8use crate::data_contract::v1::DataContractV1;
9use crate::data_contract::{
10 DataContract, DefinitionName, DocumentName, GroupContractPosition, TokenContractPosition,
11 EMPTY_GROUPS, EMPTY_TOKENS,
12};
13#[cfg(feature = "json-conversion")]
14use crate::serialization::JsonConvertible;
15use crate::validation::operations::ProtocolValidationOperation;
16use crate::version::PlatformVersion;
17use crate::ProtocolError;
18use bincode::{Decode, Encode};
19use derive_more::From;
20use platform_value::{Identifier, Value};
21use platform_version::{IntoPlatformVersioned, TryFromPlatformVersioned};
22use platform_versioning::PlatformVersioned;
23#[cfg(feature = "serde-conversion")]
24use serde::{Deserialize, Serialize};
25use std::collections::BTreeMap;
26use std::fmt;
27
28pub(in crate::data_contract) mod v0;
29pub(in crate::data_contract) mod v1;
30
31pub mod property_names {
32 pub const ID: &str = "id";
33 pub const OWNER_ID: &str = "ownerId";
34 pub const VERSION: &str = "version";
35 pub const DEFINITIONS: &str = "$defs";
36}
37
38pub const CONTRACT_DESERIALIZATION_LIMIT: usize = 15000;
39
40#[derive(Debug, PartialEq, Eq, Clone, Copy)]
46pub enum DataContractMismatch {
47 Id,
49 Config,
51 Version,
53 OwnerId,
55 SchemaDefs,
57 DocumentSchemas,
59 Groups,
61 Tokens,
63 Keywords,
65 Description,
67 FormatVersionMismatch,
69 V0Mismatch,
71}
72
73impl fmt::Display for DataContractMismatch {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 let description = match self {
77 DataContractMismatch::Id => "ID fields differ",
78 DataContractMismatch::Config => "Config fields differ",
79 DataContractMismatch::Version => "Version fields differ",
80 DataContractMismatch::OwnerId => "Owner ID fields differ",
81 DataContractMismatch::SchemaDefs => "Schema definitions differ",
82 DataContractMismatch::DocumentSchemas => "Document schemas differ",
83 DataContractMismatch::Groups => "Groups differ",
84 DataContractMismatch::Tokens => "Tokens differ",
85 DataContractMismatch::Keywords => "Keywords differ",
86 DataContractMismatch::Description => "Description fields differ",
87 DataContractMismatch::FormatVersionMismatch => {
88 "Serialization format versions differ (e.g., V0 vs V1)"
89 }
90 DataContractMismatch::V0Mismatch => "V0 versions differ",
91 };
92 write!(f, "{}", description)
93 }
94}
95
96#[cfg_attr(
97 all(feature = "json-conversion", feature = "serde-conversion"),
98 derive(JsonConvertible)
99)]
100#[derive(Debug, Clone, Encode, Decode, PartialEq, PlatformVersioned, From)]
101#[cfg_attr(
102 feature = "serde-conversion",
103 derive(Serialize, Deserialize),
104 serde(tag = "$formatVersion")
105)]
106pub enum DataContractInSerializationFormat {
107 #[cfg_attr(feature = "serde-conversion", serde(rename = "0"))]
108 V0(DataContractInSerializationFormatV0),
109 #[cfg_attr(feature = "serde-conversion", serde(rename = "1"))]
110 V1(DataContractInSerializationFormatV1),
111}
112
113impl DataContractInSerializationFormat {
114 pub fn id(&self) -> Identifier {
116 match self {
117 DataContractInSerializationFormat::V0(v0) => v0.id,
118 DataContractInSerializationFormat::V1(v1) => v1.id,
119 }
120 }
121
122 pub fn owner_id(&self) -> Identifier {
124 match self {
125 DataContractInSerializationFormat::V0(v0) => v0.owner_id,
126 DataContractInSerializationFormat::V1(v1) => v1.owner_id,
127 }
128 }
129
130 pub fn document_schemas(&self) -> &BTreeMap<DocumentName, Value> {
131 match self {
132 DataContractInSerializationFormat::V0(v0) => &v0.document_schemas,
133 DataContractInSerializationFormat::V1(v1) => &v1.document_schemas,
134 }
135 }
136
137 pub fn document_schemas_mut(&mut self) -> &mut BTreeMap<DocumentName, Value> {
138 match self {
139 DataContractInSerializationFormat::V0(v0) => &mut v0.document_schemas,
140 DataContractInSerializationFormat::V1(v1) => &mut v1.document_schemas,
141 }
142 }
143
144 pub fn schema_defs(&self) -> Option<&BTreeMap<DefinitionName, Value>> {
145 match self {
146 DataContractInSerializationFormat::V0(v0) => v0.schema_defs.as_ref(),
147 DataContractInSerializationFormat::V1(v1) => v1.schema_defs.as_ref(),
148 }
149 }
150
151 pub fn version(&self) -> u32 {
152 match self {
153 DataContractInSerializationFormat::V0(v0) => v0.version,
154 DataContractInSerializationFormat::V1(v1) => v1.version,
155 }
156 }
157
158 pub fn config(&self) -> &DataContractConfig {
160 match self {
161 DataContractInSerializationFormat::V0(v0) => &v0.config,
162 DataContractInSerializationFormat::V1(v1) => &v1.config,
163 }
164 }
165
166 pub fn groups(&self) -> &BTreeMap<GroupContractPosition, Group> {
167 match self {
168 DataContractInSerializationFormat::V0(_) => &EMPTY_GROUPS,
169 DataContractInSerializationFormat::V1(v1) => &v1.groups,
170 }
171 }
172 pub fn tokens(&self) -> &BTreeMap<TokenContractPosition, TokenConfiguration> {
173 match self {
174 DataContractInSerializationFormat::V0(_) => &EMPTY_TOKENS,
175 DataContractInSerializationFormat::V1(v1) => &v1.tokens,
176 }
177 }
178
179 pub fn keywords(&self) -> &Vec<String> {
180 match self {
181 DataContractInSerializationFormat::V0(_) => &EMPTY_KEYWORDS,
182 DataContractInSerializationFormat::V1(v1) => &v1.keywords,
183 }
184 }
185
186 pub fn description(&self) -> &Option<String> {
187 match self {
188 DataContractInSerializationFormat::V0(_) => &None,
189 DataContractInSerializationFormat::V1(v1) => &v1.description,
190 }
191 }
192
193 pub fn first_mismatch(&self, other: &Self) -> Option<DataContractMismatch> {
206 match (self, other) {
207 (
208 DataContractInSerializationFormat::V0(v0_self),
209 DataContractInSerializationFormat::V0(v0_other),
210 ) => {
211 if v0_self != v0_other {
212 Some(DataContractMismatch::V0Mismatch)
213 } else {
214 None
215 }
216 }
217 (
218 DataContractInSerializationFormat::V1(v1_self),
219 DataContractInSerializationFormat::V1(v1_other),
220 ) => {
221 if v1_self.id != v1_other.id {
222 Some(DataContractMismatch::Id)
223 } else if v1_self.config != v1_other.config {
224 Some(DataContractMismatch::Config)
225 } else if v1_self.version != v1_other.version {
226 Some(DataContractMismatch::Version)
227 } else if v1_self.owner_id != v1_other.owner_id {
228 Some(DataContractMismatch::OwnerId)
229 } else if v1_self.schema_defs != v1_other.schema_defs {
230 Some(DataContractMismatch::SchemaDefs)
231 } else if v1_self.document_schemas != v1_other.document_schemas {
232 Some(DataContractMismatch::DocumentSchemas)
233 } else if v1_self.groups != v1_other.groups {
234 Some(DataContractMismatch::Groups)
235 } else if v1_self.tokens != v1_other.tokens {
236 Some(DataContractMismatch::Tokens)
237 } else if v1_self.keywords.len() != v1_other.keywords.len()
238 || v1_self
239 .keywords
240 .iter()
241 .zip(v1_other.keywords.iter())
242 .any(|(a, b)| a.to_lowercase() != b.to_lowercase())
243 {
244 Some(DataContractMismatch::Keywords)
245 } else if v1_self.description != v1_other.description {
246 Some(DataContractMismatch::Description)
247 } else {
248 None
249 }
250 }
251 _ => Some(DataContractMismatch::FormatVersionMismatch),
252 }
253 }
254}
255
256impl TryFromPlatformVersioned<DataContractV0> for DataContractInSerializationFormat {
257 type Error = ProtocolError;
258
259 fn try_from_platform_versioned(
260 value: DataContractV0,
261 platform_version: &PlatformVersion,
262 ) -> Result<Self, Self::Error> {
263 match platform_version
264 .dpp
265 .contract_versions
266 .contract_serialization_version
267 .default_current_version
268 {
269 0 => {
270 let v0_format: DataContractInSerializationFormatV0 =
271 DataContract::V0(value).into_platform_versioned(platform_version);
272 Ok(v0_format.into())
273 }
274 1 => {
275 let v1_format: DataContractInSerializationFormatV1 =
276 DataContract::V0(value).into_platform_versioned(platform_version);
277 Ok(v1_format.into())
278 }
279 version => Err(ProtocolError::UnknownVersionMismatch {
280 method: "DataContract::serialize_to_default_current_version".to_string(),
281 known_versions: vec![0, 1],
282 received: version,
283 }),
284 }
285 }
286}
287
288impl TryFromPlatformVersioned<&DataContractV0> for DataContractInSerializationFormat {
289 type Error = ProtocolError;
290
291 fn try_from_platform_versioned(
292 value: &DataContractV0,
293 platform_version: &PlatformVersion,
294 ) -> Result<Self, Self::Error> {
295 match platform_version
296 .dpp
297 .contract_versions
298 .contract_serialization_version
299 .default_current_version
300 {
301 0 => {
302 let v0_format: DataContractInSerializationFormatV0 =
303 DataContract::V0(value.to_owned()).into_platform_versioned(platform_version);
304 Ok(v0_format.into())
305 }
306 1 => {
307 let v1_format: DataContractInSerializationFormatV1 =
308 DataContract::V0(value.to_owned()).into_platform_versioned(platform_version);
309 Ok(v1_format.into())
310 }
311 version => Err(ProtocolError::UnknownVersionMismatch {
312 method: "DataContract::serialize_to_default_current_version".to_string(),
313 known_versions: vec![0, 1],
314 received: version,
315 }),
316 }
317 }
318}
319
320impl TryFromPlatformVersioned<DataContractV1> for DataContractInSerializationFormat {
321 type Error = ProtocolError;
322
323 fn try_from_platform_versioned(
324 value: DataContractV1,
325 platform_version: &PlatformVersion,
326 ) -> Result<Self, Self::Error> {
327 match platform_version
328 .dpp
329 .contract_versions
330 .contract_serialization_version
331 .default_current_version
332 {
333 0 => {
334 let v0_format: DataContractInSerializationFormatV0 =
335 DataContract::V1(value).into_platform_versioned(platform_version);
336 Ok(v0_format.into())
337 }
338 1 => {
339 let v1_format: DataContractInSerializationFormatV1 =
340 DataContract::V1(value).into_platform_versioned(platform_version);
341 Ok(v1_format.into())
342 }
343 version => Err(ProtocolError::UnknownVersionMismatch {
344 method: "DataContract::serialize_to_default_current_version".to_string(),
345 known_versions: vec![0, 1],
346 received: version,
347 }),
348 }
349 }
350}
351
352impl TryFromPlatformVersioned<&DataContractV1> for DataContractInSerializationFormat {
353 type Error = ProtocolError;
354
355 fn try_from_platform_versioned(
356 value: &DataContractV1,
357 platform_version: &PlatformVersion,
358 ) -> Result<Self, Self::Error> {
359 match platform_version
360 .dpp
361 .contract_versions
362 .contract_serialization_version
363 .default_current_version
364 {
365 0 => {
366 let v0_format: DataContractInSerializationFormatV0 =
367 DataContract::V1(value.to_owned()).into_platform_versioned(platform_version);
368 Ok(v0_format.into())
369 }
370 1 => {
371 let v1_format: DataContractInSerializationFormatV1 =
372 DataContract::V1(value.to_owned()).into_platform_versioned(platform_version);
373 Ok(v1_format.into())
374 }
375 version => Err(ProtocolError::UnknownVersionMismatch {
376 method: "DataContract::serialize_to_default_current_version".to_string(),
377 known_versions: vec![0, 1],
378 received: version,
379 }),
380 }
381 }
382}
383
384impl TryFromPlatformVersioned<&DataContract> for DataContractInSerializationFormat {
385 type Error = ProtocolError;
386
387 fn try_from_platform_versioned(
388 value: &DataContract,
389 platform_version: &PlatformVersion,
390 ) -> Result<Self, Self::Error> {
391 match platform_version
392 .dpp
393 .contract_versions
394 .contract_serialization_version
395 .default_current_version
396 {
397 0 => {
398 let v0_format: DataContractInSerializationFormatV0 =
399 value.clone().into_platform_versioned(platform_version);
400 Ok(v0_format.into())
401 }
402 1 => {
403 let v1_format: DataContractInSerializationFormatV1 =
404 value.clone().into_platform_versioned(platform_version);
405 Ok(v1_format.into())
406 }
407 version => Err(ProtocolError::UnknownVersionMismatch {
408 method: "DataContract::serialize_to_default_current_version".to_string(),
409 known_versions: vec![0, 1],
410 received: version,
411 }),
412 }
413 }
414}
415
416impl TryFromPlatformVersioned<DataContract> for DataContractInSerializationFormat {
417 type Error = ProtocolError;
418
419 fn try_from_platform_versioned(
420 value: DataContract,
421 platform_version: &PlatformVersion,
422 ) -> Result<Self, Self::Error> {
423 match platform_version
424 .dpp
425 .contract_versions
426 .contract_serialization_version
427 .default_current_version
428 {
429 0 => {
430 let v0_format: DataContractInSerializationFormatV0 =
431 value.into_platform_versioned(platform_version);
432 Ok(v0_format.into())
433 }
434 1 => {
435 let v1_format: DataContractInSerializationFormatV1 =
436 value.into_platform_versioned(platform_version);
437 Ok(v1_format.into())
438 }
439 version => Err(ProtocolError::UnknownVersionMismatch {
440 method: "DataContract::serialize_consume_to_default_current_version".to_string(),
441 known_versions: vec![0, 1],
442 received: version,
443 }),
444 }
445 }
446}
447
448impl DataContract {
449 pub fn try_from_platform_versioned(
450 value: DataContractInSerializationFormat,
451 full_validation: bool,
452 validation_operations: &mut Vec<ProtocolValidationOperation>,
453 platform_version: &PlatformVersion,
454 ) -> Result<Self, ProtocolError> {
455 match platform_version
456 .dpp
457 .contract_versions
458 .contract_structure_version
459 {
460 0 => DataContractV0::try_from_platform_versioned(
461 value,
462 full_validation,
463 validation_operations,
464 platform_version,
465 )
466 .map(|contract| contract.into()),
467 1 => DataContractV1::try_from_platform_versioned(
468 value,
469 full_validation,
470 validation_operations,
471 platform_version,
472 )
473 .map(|contract| contract.into()),
474 version => Err(ProtocolError::UnknownVersionMismatch {
475 method: "DataContract::try_from_platform_versioned".to_string(),
476 known_versions: vec![0, 1],
477 received: version,
478 }),
479 }
480 }
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486 use crate::data_contract::config::v0::DataContractConfigV0;
487 use crate::data_contract::config::v1::DataContractConfigV1;
488 use crate::data_contract::group::v0::GroupV0;
489 use crate::data_contract::serialized_version::v0::DataContractInSerializationFormatV0;
490 use crate::data_contract::serialized_version::v1::DataContractInSerializationFormatV1;
491 use platform_value::Identifier;
492 use std::collections::BTreeMap;
493
494 fn make_v0() -> DataContractInSerializationFormatV0 {
496 DataContractInSerializationFormatV0 {
497 id: Identifier::default(),
498 config: DataContractConfig::V0(DataContractConfigV0::default()),
499 version: 1,
500 owner_id: Identifier::default(),
501 schema_defs: None,
502 document_schemas: BTreeMap::new(),
503 }
504 }
505
506 fn make_v1() -> DataContractInSerializationFormatV1 {
508 DataContractInSerializationFormatV1 {
509 id: Identifier::default(),
510 config: DataContractConfig::V1(DataContractConfigV1::default()),
511 version: 1,
512 owner_id: Identifier::default(),
513 schema_defs: None,
514 document_schemas: BTreeMap::new(),
515 created_at: None,
516 updated_at: None,
517 created_at_block_height: None,
518 updated_at_block_height: None,
519 created_at_epoch: None,
520 updated_at_epoch: None,
521 groups: BTreeMap::new(),
522 tokens: BTreeMap::new(),
523 keywords: vec![],
524 description: None,
525 }
526 }
527
528 #[test]
533 fn first_mismatch_v0_v0_identical_returns_none() {
534 let a = DataContractInSerializationFormat::V0(make_v0());
535 let b = DataContractInSerializationFormat::V0(make_v0());
536 assert_eq!(a.first_mismatch(&b), None);
537 }
538
539 #[test]
540 fn first_mismatch_v0_v0_different_id() {
541 let mut v0_b = make_v0();
542 v0_b.id = Identifier::from([1u8; 32]);
543 let a = DataContractInSerializationFormat::V0(make_v0());
544 let b = DataContractInSerializationFormat::V0(v0_b);
545 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::V0Mismatch));
546 }
547
548 #[test]
549 fn first_mismatch_v0_v0_different_config() {
550 let mut v0_b = make_v0();
551 let mut cfg = DataContractConfigV0::default();
552 cfg.readonly = !cfg.readonly;
553 v0_b.config = DataContractConfig::V0(cfg);
554 let a = DataContractInSerializationFormat::V0(make_v0());
555 let b = DataContractInSerializationFormat::V0(v0_b);
556 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::V0Mismatch));
557 }
558
559 #[test]
560 fn first_mismatch_v0_v0_different_version() {
561 let mut v0_b = make_v0();
562 v0_b.version = 99;
563 let a = DataContractInSerializationFormat::V0(make_v0());
564 let b = DataContractInSerializationFormat::V0(v0_b);
565 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::V0Mismatch));
566 }
567
568 #[test]
569 fn first_mismatch_v0_v0_different_owner_id() {
570 let mut v0_b = make_v0();
571 v0_b.owner_id = Identifier::from([2u8; 32]);
572 let a = DataContractInSerializationFormat::V0(make_v0());
573 let b = DataContractInSerializationFormat::V0(v0_b);
574 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::V0Mismatch));
575 }
576
577 #[test]
578 fn first_mismatch_v0_v0_different_document_schemas() {
579 let mut v0_b = make_v0();
580 v0_b.document_schemas
581 .insert("doc".to_string(), Value::Bool(true));
582 let a = DataContractInSerializationFormat::V0(make_v0());
583 let b = DataContractInSerializationFormat::V0(v0_b);
584 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::V0Mismatch));
585 }
586
587 #[test]
592 fn first_mismatch_v0_v1_returns_format_version_mismatch() {
593 let a = DataContractInSerializationFormat::V0(make_v0());
594 let b = DataContractInSerializationFormat::V1(make_v1());
595 assert_eq!(
596 a.first_mismatch(&b),
597 Some(DataContractMismatch::FormatVersionMismatch)
598 );
599 }
600
601 #[test]
602 fn first_mismatch_v1_v0_returns_format_version_mismatch() {
603 let a = DataContractInSerializationFormat::V1(make_v1());
604 let b = DataContractInSerializationFormat::V0(make_v0());
605 assert_eq!(
606 a.first_mismatch(&b),
607 Some(DataContractMismatch::FormatVersionMismatch)
608 );
609 }
610
611 #[test]
616 fn first_mismatch_v1_v1_identical_returns_none() {
617 let a = DataContractInSerializationFormat::V1(make_v1());
618 let b = DataContractInSerializationFormat::V1(make_v1());
619 assert_eq!(a.first_mismatch(&b), None);
620 }
621
622 #[test]
627 fn first_mismatch_v1_v1_different_id() {
628 let mut v1_b = make_v1();
629 v1_b.id = Identifier::from([1u8; 32]);
630 let a = DataContractInSerializationFormat::V1(make_v1());
631 let b = DataContractInSerializationFormat::V1(v1_b);
632 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Id));
633 }
634
635 #[test]
636 fn first_mismatch_v1_v1_different_config() {
637 let mut v1_b = make_v1();
638 let mut cfg = DataContractConfigV1::default();
639 cfg.readonly = !cfg.readonly;
640 v1_b.config = DataContractConfig::V1(cfg);
641 let a = DataContractInSerializationFormat::V1(make_v1());
642 let b = DataContractInSerializationFormat::V1(v1_b);
643 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Config));
644 }
645
646 #[test]
647 fn first_mismatch_v1_v1_different_version() {
648 let mut v1_b = make_v1();
649 v1_b.version = 42;
650 let a = DataContractInSerializationFormat::V1(make_v1());
651 let b = DataContractInSerializationFormat::V1(v1_b);
652 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Version));
653 }
654
655 #[test]
656 fn first_mismatch_v1_v1_different_owner_id() {
657 let mut v1_b = make_v1();
658 v1_b.owner_id = Identifier::from([3u8; 32]);
659 let a = DataContractInSerializationFormat::V1(make_v1());
660 let b = DataContractInSerializationFormat::V1(v1_b);
661 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::OwnerId));
662 }
663
664 #[test]
665 fn first_mismatch_v1_v1_different_schema_defs() {
666 let mut v1_b = make_v1();
667 let mut defs = BTreeMap::new();
668 defs.insert("someDef".to_string(), Value::Bool(true));
669 v1_b.schema_defs = Some(defs);
670 let a = DataContractInSerializationFormat::V1(make_v1());
671 let b = DataContractInSerializationFormat::V1(v1_b);
672 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::SchemaDefs));
673 }
674
675 #[test]
676 fn first_mismatch_v1_v1_different_document_schemas() {
677 let mut v1_b = make_v1();
678 v1_b.document_schemas
679 .insert("doc".to_string(), Value::U64(1));
680 let a = DataContractInSerializationFormat::V1(make_v1());
681 let b = DataContractInSerializationFormat::V1(v1_b);
682 assert_eq!(
683 a.first_mismatch(&b),
684 Some(DataContractMismatch::DocumentSchemas)
685 );
686 }
687
688 #[test]
689 fn first_mismatch_v1_v1_different_groups() {
690 let mut v1_b = make_v1();
691 v1_b.groups.insert(
692 0,
693 Group::V0(GroupV0 {
694 members: Default::default(),
695 required_power: 1,
696 }),
697 );
698 let a = DataContractInSerializationFormat::V1(make_v1());
699 let b = DataContractInSerializationFormat::V1(v1_b);
700 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Groups));
701 }
702
703 #[test]
704 fn first_mismatch_v1_v1_different_tokens() {
705 let mut v1_b = make_v1();
706 v1_b.tokens.insert(
707 0,
708 TokenConfiguration::V0(
709 crate::data_contract::associated_token::token_configuration::v0::TokenConfigurationV0::default_most_restrictive(),
710 ),
711 );
712 let a = DataContractInSerializationFormat::V1(make_v1());
713 let b = DataContractInSerializationFormat::V1(v1_b);
714 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Tokens));
715 }
716
717 #[test]
718 fn first_mismatch_v1_v1_different_keywords() {
719 let mut v1_b = make_v1();
720 v1_b.keywords = vec!["test".to_string()];
721 let a = DataContractInSerializationFormat::V1(make_v1());
722 let b = DataContractInSerializationFormat::V1(v1_b);
723 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Keywords));
724 }
725
726 #[test]
727 fn first_mismatch_v1_v1_keywords_case_insensitive_match() {
728 let mut v1_a = make_v1();
729 v1_a.keywords = vec!["Test".to_string()];
730 let mut v1_b = make_v1();
731 v1_b.keywords = vec!["test".to_string()];
732 let a = DataContractInSerializationFormat::V1(v1_a);
733 let b = DataContractInSerializationFormat::V1(v1_b);
734 assert_eq!(a.first_mismatch(&b), None);
736 }
737
738 #[test]
739 fn first_mismatch_v1_v1_keywords_different_length() {
740 let mut v1_a = make_v1();
741 v1_a.keywords = vec!["a".to_string()];
742 let mut v1_b = make_v1();
743 v1_b.keywords = vec!["a".to_string(), "b".to_string()];
744 let a = DataContractInSerializationFormat::V1(v1_a);
745 let b = DataContractInSerializationFormat::V1(v1_b);
746 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Keywords));
747 }
748
749 #[test]
750 fn first_mismatch_v1_v1_different_description() {
751 let mut v1_b = make_v1();
752 v1_b.description = Some("a description".to_string());
753 let a = DataContractInSerializationFormat::V1(make_v1());
754 let b = DataContractInSerializationFormat::V1(v1_b);
755 assert_eq!(
756 a.first_mismatch(&b),
757 Some(DataContractMismatch::Description)
758 );
759 }
760
761 #[test]
766 fn first_mismatch_v1_v1_id_takes_priority_over_config() {
767 let mut v1_b = make_v1();
768 v1_b.id = Identifier::from([5u8; 32]);
769 let mut cfg = DataContractConfigV1::default();
770 cfg.readonly = !cfg.readonly;
771 v1_b.config = DataContractConfig::V1(cfg);
772 let a = DataContractInSerializationFormat::V1(make_v1());
773 let b = DataContractInSerializationFormat::V1(v1_b);
774 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Id));
776 }
777
778 #[test]
783 fn data_contract_mismatch_display() {
784 assert_eq!(format!("{}", DataContractMismatch::Id), "ID fields differ");
785 assert_eq!(
786 format!("{}", DataContractMismatch::FormatVersionMismatch),
787 "Serialization format versions differ (e.g., V0 vs V1)"
788 );
789 assert_eq!(
790 format!("{}", DataContractMismatch::V0Mismatch),
791 "V0 versions differ"
792 );
793 assert_eq!(format!("{}", DataContractMismatch::Tokens), "Tokens differ");
794 assert_eq!(
795 format!("{}", DataContractMismatch::Keywords),
796 "Keywords differ"
797 );
798 assert_eq!(
799 format!("{}", DataContractMismatch::Description),
800 "Description fields differ"
801 );
802 }
803
804 #[test]
809 fn accessor_id_v0() {
810 let v0 = make_v0();
811 let expected_id = v0.id;
812 let format = DataContractInSerializationFormat::V0(v0);
813 assert_eq!(format.id(), expected_id);
814 }
815
816 #[test]
817 fn accessor_id_v1() {
818 let v1 = make_v1();
819 let expected_id = v1.id;
820 let format = DataContractInSerializationFormat::V1(v1);
821 assert_eq!(format.id(), expected_id);
822 }
823
824 #[test]
825 fn accessor_owner_id_v0() {
826 let mut v0 = make_v0();
827 v0.owner_id = Identifier::from([7u8; 32]);
828 let expected = v0.owner_id;
829 let format = DataContractInSerializationFormat::V0(v0);
830 assert_eq!(format.owner_id(), expected);
831 }
832
833 #[test]
834 fn accessor_version_v0() {
835 let mut v0 = make_v0();
836 v0.version = 10;
837 let format = DataContractInSerializationFormat::V0(v0);
838 assert_eq!(format.version(), 10);
839 }
840
841 #[test]
842 fn accessor_version_v1() {
843 let mut v1 = make_v1();
844 v1.version = 20;
845 let format = DataContractInSerializationFormat::V1(v1);
846 assert_eq!(format.version(), 20);
847 }
848
849 #[test]
850 fn accessor_groups_v0_returns_empty() {
851 let format = DataContractInSerializationFormat::V0(make_v0());
852 assert!(format.groups().is_empty());
853 }
854
855 #[test]
856 fn accessor_tokens_v0_returns_empty() {
857 let format = DataContractInSerializationFormat::V0(make_v0());
858 assert!(format.tokens().is_empty());
859 }
860
861 #[test]
862 fn accessor_keywords_v0_returns_empty() {
863 let format = DataContractInSerializationFormat::V0(make_v0());
864 assert!(format.keywords().is_empty());
865 }
866
867 #[test]
868 fn accessor_description_v0_returns_none() {
869 let format = DataContractInSerializationFormat::V0(make_v0());
870 assert_eq!(format.description(), &None);
871 }
872
873 #[test]
874 fn accessor_keywords_v1() {
875 let mut v1 = make_v1();
876 v1.keywords = vec!["hello".to_string()];
877 let format = DataContractInSerializationFormat::V1(v1);
878 assert_eq!(format.keywords(), &vec!["hello".to_string()]);
879 }
880
881 #[test]
882 fn accessor_description_v1_some() {
883 let mut v1 = make_v1();
884 v1.description = Some("desc".to_string());
885 let format = DataContractInSerializationFormat::V1(v1);
886 assert_eq!(format.description(), &Some("desc".to_string()));
887 }
888
889 #[test]
890 fn accessor_document_schemas_v0() {
891 let mut v0 = make_v0();
892 v0.document_schemas
893 .insert("note".to_string(), Value::Bool(true));
894 let format = DataContractInSerializationFormat::V0(v0);
895 assert_eq!(format.document_schemas().len(), 1);
896 assert!(format.document_schemas().contains_key("note"));
897 }
898
899 #[test]
900 fn accessor_schema_defs_v0_none() {
901 let format = DataContractInSerializationFormat::V0(make_v0());
902 assert!(format.schema_defs().is_none());
903 }
904
905 #[test]
906 fn accessor_schema_defs_v1_some() {
907 let mut v1 = make_v1();
908 let mut defs = BTreeMap::new();
909 defs.insert("def1".to_string(), Value::Null);
910 v1.schema_defs = Some(defs);
911 let format = DataContractInSerializationFormat::V1(v1);
912 assert!(format.schema_defs().is_some());
913 assert!(format.schema_defs().unwrap().contains_key("def1"));
914 }
915
916 #[test]
921 fn try_from_platform_versioned_data_contract_v0_version_0() {
922 let platform_version = PlatformVersion::first();
923 let v0 = DataContractV0 {
925 id: Identifier::from([10u8; 32]),
926 config: DataContractConfig::V0(DataContractConfigV0::default()),
927 version: 1,
928 owner_id: Identifier::from([20u8; 32]),
929 schema_defs: None,
930 document_types: BTreeMap::new(),
931 metadata: None,
932 };
933 let result = DataContractInSerializationFormat::try_from_platform_versioned(
934 v0.clone(),
935 platform_version,
936 );
937 assert!(result.is_ok());
938 let format = result.unwrap();
939 assert!(matches!(format, DataContractInSerializationFormat::V0(_)));
940 assert_eq!(format.id(), Identifier::from([10u8; 32]));
941 assert_eq!(format.owner_id(), Identifier::from([20u8; 32]));
942 }
943
944 #[test]
945 fn try_from_platform_versioned_data_contract_v0_ref_version_0() {
946 let platform_version = PlatformVersion::first();
947 let v0 = DataContractV0 {
948 id: Identifier::from([11u8; 32]),
949 config: DataContractConfig::V0(DataContractConfigV0::default()),
950 version: 2,
951 owner_id: Identifier::from([22u8; 32]),
952 schema_defs: None,
953 document_types: BTreeMap::new(),
954 metadata: None,
955 };
956 let result =
957 DataContractInSerializationFormat::try_from_platform_versioned(&v0, platform_version);
958 assert!(result.is_ok());
959 let format = result.unwrap();
960 assert!(matches!(format, DataContractInSerializationFormat::V0(_)));
961 assert_eq!(format.version(), 2);
962 }
963
964 #[test]
965 fn try_from_platform_versioned_data_contract_v0_version_1() {
966 let platform_version = PlatformVersion::latest();
967 let v0 = DataContractV0 {
969 id: Identifier::from([10u8; 32]),
970 config: DataContractConfig::V0(DataContractConfigV0::default()),
971 version: 1,
972 owner_id: Identifier::from([20u8; 32]),
973 schema_defs: None,
974 document_types: BTreeMap::new(),
975 metadata: None,
976 };
977 let result = DataContractInSerializationFormat::try_from_platform_versioned(
978 v0.clone(),
979 platform_version,
980 );
981 assert!(result.is_ok());
982 let format = result.unwrap();
983 assert!(matches!(format, DataContractInSerializationFormat::V1(_)));
984 }
985
986 #[test]
991 fn try_from_platform_versioned_data_contract_v1_version_0() {
992 let platform_version = PlatformVersion::first();
993 let v1 = DataContractV1 {
994 id: Identifier::from([10u8; 32]),
995 config: DataContractConfig::V0(DataContractConfigV0::default()),
996 version: 1,
997 owner_id: Identifier::from([20u8; 32]),
998 schema_defs: None,
999 document_types: BTreeMap::new(),
1000 created_at: None,
1001 updated_at: None,
1002 created_at_block_height: None,
1003 updated_at_block_height: None,
1004 created_at_epoch: None,
1005 updated_at_epoch: None,
1006 groups: BTreeMap::new(),
1007 tokens: BTreeMap::new(),
1008 keywords: vec![],
1009 description: None,
1010 };
1011 let result = DataContractInSerializationFormat::try_from_platform_versioned(
1012 v1.clone(),
1013 platform_version,
1014 );
1015 assert!(result.is_ok());
1016 let format = result.unwrap();
1017 assert!(matches!(format, DataContractInSerializationFormat::V0(_)));
1018 }
1019
1020 #[test]
1021 fn try_from_platform_versioned_data_contract_v1_version_1() {
1022 let platform_version = PlatformVersion::latest();
1023 let v1 = DataContractV1 {
1024 id: Identifier::from([10u8; 32]),
1025 config: DataContractConfig::V1(DataContractConfigV1::default()),
1026 version: 1,
1027 owner_id: Identifier::from([20u8; 32]),
1028 schema_defs: None,
1029 document_types: BTreeMap::new(),
1030 created_at: None,
1031 updated_at: None,
1032 created_at_block_height: None,
1033 updated_at_block_height: None,
1034 created_at_epoch: None,
1035 updated_at_epoch: None,
1036 groups: BTreeMap::new(),
1037 tokens: BTreeMap::new(),
1038 keywords: vec![],
1039 description: None,
1040 };
1041 let result = DataContractInSerializationFormat::try_from_platform_versioned(
1042 v1.clone(),
1043 platform_version,
1044 );
1045 assert!(result.is_ok());
1046 let format = result.unwrap();
1047 assert!(matches!(format, DataContractInSerializationFormat::V1(_)));
1048 }
1049
1050 #[test]
1051 fn try_from_platform_versioned_data_contract_v1_ref_version_1() {
1052 let platform_version = PlatformVersion::latest();
1053 let v1 = DataContractV1 {
1054 id: Identifier::from([10u8; 32]),
1055 config: DataContractConfig::V1(DataContractConfigV1::default()),
1056 version: 3,
1057 owner_id: Identifier::from([20u8; 32]),
1058 schema_defs: None,
1059 document_types: BTreeMap::new(),
1060 created_at: None,
1061 updated_at: None,
1062 created_at_block_height: None,
1063 updated_at_block_height: None,
1064 created_at_epoch: None,
1065 updated_at_epoch: None,
1066 groups: BTreeMap::new(),
1067 tokens: BTreeMap::new(),
1068 keywords: vec![],
1069 description: None,
1070 };
1071 let result =
1072 DataContractInSerializationFormat::try_from_platform_versioned(&v1, platform_version);
1073 assert!(result.is_ok());
1074 let format = result.unwrap();
1075 assert!(matches!(format, DataContractInSerializationFormat::V1(_)));
1076 assert_eq!(format.version(), 3);
1077 }
1078
1079 #[test]
1084 fn try_from_platform_versioned_data_contract_ref_version_0() {
1085 let platform_version = PlatformVersion::first();
1086 let contract = DataContract::V0(DataContractV0 {
1087 id: Identifier::from([10u8; 32]),
1088 config: DataContractConfig::V0(DataContractConfigV0::default()),
1089 version: 1,
1090 owner_id: Identifier::from([20u8; 32]),
1091 schema_defs: None,
1092 document_types: BTreeMap::new(),
1093 metadata: None,
1094 });
1095 let result = DataContractInSerializationFormat::try_from_platform_versioned(
1096 &contract,
1097 platform_version,
1098 );
1099 assert!(result.is_ok());
1100 assert!(matches!(
1101 result.unwrap(),
1102 DataContractInSerializationFormat::V0(_)
1103 ));
1104 }
1105
1106 #[test]
1107 fn try_from_platform_versioned_data_contract_owned_version_1() {
1108 let platform_version = PlatformVersion::latest();
1109 let contract = DataContract::V0(DataContractV0 {
1110 id: Identifier::from([10u8; 32]),
1111 config: DataContractConfig::V0(DataContractConfigV0::default()),
1112 version: 1,
1113 owner_id: Identifier::from([20u8; 32]),
1114 schema_defs: None,
1115 document_types: BTreeMap::new(),
1116 metadata: None,
1117 });
1118 let result = DataContractInSerializationFormat::try_from_platform_versioned(
1119 contract,
1120 platform_version,
1121 );
1122 assert!(result.is_ok());
1123 assert!(matches!(
1124 result.unwrap(),
1125 DataContractInSerializationFormat::V1(_)
1126 ));
1127 }
1128
1129 #[test]
1134 fn first_platform_version_uses_serialization_version_0() {
1135 let pv = PlatformVersion::first();
1136 assert_eq!(
1137 pv.dpp
1138 .contract_versions
1139 .contract_serialization_version
1140 .default_current_version,
1141 0
1142 );
1143 }
1144
1145 #[test]
1146 fn latest_platform_version_uses_serialization_version_1() {
1147 let pv = PlatformVersion::latest();
1148 assert_eq!(
1149 pv.dpp
1150 .contract_versions
1151 .contract_serialization_version
1152 .default_current_version,
1153 1
1154 );
1155 }
1156
1157 #[test]
1158 fn first_platform_version_uses_contract_structure_0() {
1159 let pv = PlatformVersion::first();
1160 assert_eq!(pv.dpp.contract_versions.contract_structure_version, 0);
1161 }
1162
1163 #[test]
1164 fn latest_platform_version_uses_contract_structure_1() {
1165 let pv = PlatformVersion::latest();
1166 assert_eq!(pv.dpp.contract_versions.contract_structure_version, 1);
1167 }
1168}