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