dpp/data_contract/config/
mod.rs

1mod fields;
2mod methods;
3pub mod v0;
4pub mod v1;
5
6use crate::data_contract::config::v1::{
7    DataContractConfigGettersV1, DataContractConfigSettersV1, DataContractConfigV1,
8};
9use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements;
10#[cfg(feature = "json-conversion")]
11use crate::serialization::JsonConvertible;
12use crate::version::PlatformVersion;
13use crate::ProtocolError;
14use bincode::{Decode, Encode};
15use derive_more::From;
16pub use fields::*;
17use platform_value::Value;
18use serde::{Deserialize, Serialize};
19use std::collections::BTreeMap;
20use v0::{DataContractConfigGettersV0, DataContractConfigSettersV0, DataContractConfigV0};
21
22#[cfg_attr(feature = "json-conversion", derive(JsonConvertible))]
23#[derive(Serialize, Deserialize, Encode, Decode, Debug, Clone, Copy, PartialEq, Eq, From)]
24#[serde(tag = "$formatVersion")]
25pub enum DataContractConfig {
26    #[serde(rename = "0")]
27    V0(DataContractConfigV0),
28    #[serde(rename = "1")]
29    V1(DataContractConfigV1),
30}
31
32impl DataContractConfig {
33    pub fn version(&self) -> u16 {
34        match self {
35            DataContractConfig::V0(_) => 0,
36            DataContractConfig::V1(_) => 1,
37        }
38    }
39
40    pub fn default_for_version(
41        platform_version: &PlatformVersion,
42    ) -> Result<DataContractConfig, ProtocolError> {
43        match platform_version
44            .dpp
45            .contract_versions
46            .config
47            .default_current_version
48        {
49            0 => Ok(DataContractConfigV0::default().into()),
50            1 => Ok(DataContractConfigV1::default().into()),
51            version => Err(ProtocolError::UnknownVersionMismatch {
52                method: "DataContractConfig::default_for_version".to_string(),
53                known_versions: vec![0, 1],
54                received: version,
55            }),
56        }
57    }
58
59    /// Adjusts the current `DataContractConfig` to be valid for the provided platform version.
60    ///
61    /// This replaces the internal version with the `default_current_version` defined in the platform version's
62    /// feature bounds for contract config.
63    pub fn config_valid_for_platform_version(
64        self,
65        platform_version: &PlatformVersion,
66    ) -> DataContractConfig {
67        match self {
68            DataContractConfig::V0(v0) => DataContractConfig::V0(v0),
69            DataContractConfig::V1(v1) => {
70                if platform_version.dpp.contract_versions.config.max_version == 0 {
71                    DataContractConfig::V0(v1.into())
72                } else {
73                    self
74                }
75            }
76        }
77    }
78
79    pub fn from_value(
80        value: Value,
81        platform_version: &PlatformVersion,
82    ) -> Result<DataContractConfig, ProtocolError> {
83        match platform_version
84            .dpp
85            .contract_versions
86            .config
87            .default_current_version
88        {
89            0 => {
90                let config: DataContractConfigV0 = platform_value::from_value(value)?;
91                Ok(config.into())
92            }
93            1 => {
94                let config: DataContractConfigV1 = platform_value::from_value(value)?;
95                Ok(config.into())
96            }
97            version => Err(ProtocolError::UnknownVersionMismatch {
98                method: "DataContractConfig::from_value".to_string(),
99                known_versions: vec![0, 1],
100                received: version,
101            }),
102        }
103    }
104
105    // TODO: Remove, it's not using
106    /// Retrieve contract configuration properties.
107    ///
108    /// This method takes a BTreeMap representing a contract and retrieves
109    /// the configuration properties based on the values found in the map.
110    ///
111    /// The process of retrieving contract configuration properties is versioned,
112    /// and the version is determined by the platform version parameter.
113    /// If the version is not supported, an error is returned.
114    ///
115    /// # Parameters
116    ///
117    /// * `contract`: BTreeMap representing the contract.
118    /// * `platform_version`: The platform version being used.
119    ///
120    /// # Returns
121    ///
122    /// * `Result<ContractConfig, ProtocolError>`: On success, a ContractConfig.
123    ///   On failure, a ProtocolError.
124    pub(in crate::data_contract) fn get_contract_configuration_properties(
125        contract: &BTreeMap<String, Value>,
126        platform_version: &PlatformVersion,
127    ) -> Result<DataContractConfig, ProtocolError> {
128        match platform_version
129            .dpp
130            .contract_versions
131            .config
132            .default_current_version
133        {
134            0 => Ok(
135                DataContractConfigV0::get_contract_configuration_properties_v0(contract)?.into(),
136            ),
137            1 => Ok(
138                DataContractConfigV1::get_contract_configuration_properties_v1(contract)?.into(),
139            ),
140            version => Err(ProtocolError::UnknownVersionMismatch {
141                method: "DataContractConfig::get_contract_configuration_properties".to_string(),
142                known_versions: vec![0, 1],
143                received: version,
144            }),
145        }
146    }
147}
148
149impl DataContractConfigGettersV0 for DataContractConfig {
150    fn can_be_deleted(&self) -> bool {
151        match self {
152            DataContractConfig::V0(v0) => v0.can_be_deleted,
153            DataContractConfig::V1(v1) => v1.can_be_deleted,
154        }
155    }
156
157    fn readonly(&self) -> bool {
158        match self {
159            DataContractConfig::V0(v0) => v0.readonly,
160            DataContractConfig::V1(v1) => v1.readonly,
161        }
162    }
163
164    fn keeps_history(&self) -> bool {
165        match self {
166            DataContractConfig::V0(v0) => v0.keeps_history,
167            DataContractConfig::V1(v1) => v1.keeps_history,
168        }
169    }
170
171    fn documents_keep_history_contract_default(&self) -> bool {
172        match self {
173            DataContractConfig::V0(v0) => v0.documents_keep_history_contract_default,
174            DataContractConfig::V1(v1) => v1.documents_keep_history_contract_default,
175        }
176    }
177
178    fn documents_mutable_contract_default(&self) -> bool {
179        match self {
180            DataContractConfig::V0(v0) => v0.documents_mutable_contract_default,
181            DataContractConfig::V1(v1) => v1.documents_mutable_contract_default,
182        }
183    }
184
185    fn documents_can_be_deleted_contract_default(&self) -> bool {
186        match self {
187            DataContractConfig::V0(v0) => v0.documents_can_be_deleted_contract_default,
188            DataContractConfig::V1(v1) => v1.documents_can_be_deleted_contract_default,
189        }
190    }
191
192    /// Encryption key storage requirements
193    fn requires_identity_encryption_bounded_key(&self) -> Option<StorageKeyRequirements> {
194        match self {
195            DataContractConfig::V0(v0) => v0.requires_identity_encryption_bounded_key,
196            DataContractConfig::V1(v1) => v1.requires_identity_encryption_bounded_key,
197        }
198    }
199
200    /// Decryption key storage requirements
201    fn requires_identity_decryption_bounded_key(&self) -> Option<StorageKeyRequirements> {
202        match self {
203            DataContractConfig::V0(v0) => v0.requires_identity_decryption_bounded_key,
204            DataContractConfig::V1(v1) => v1.requires_identity_decryption_bounded_key,
205        }
206    }
207}
208
209impl DataContractConfigSettersV0 for DataContractConfig {
210    fn set_can_be_deleted(&mut self, value: bool) {
211        match self {
212            DataContractConfig::V0(v0) => v0.can_be_deleted = value,
213            DataContractConfig::V1(v1) => v1.can_be_deleted = value,
214        }
215    }
216
217    fn set_readonly(&mut self, value: bool) {
218        match self {
219            DataContractConfig::V0(v0) => v0.readonly = value,
220            DataContractConfig::V1(v1) => v1.readonly = value,
221        }
222    }
223
224    fn set_keeps_history(&mut self, value: bool) {
225        match self {
226            DataContractConfig::V0(v0) => v0.keeps_history = value,
227            DataContractConfig::V1(v1) => v1.keeps_history = value,
228        }
229    }
230
231    fn set_documents_keep_history_contract_default(&mut self, value: bool) {
232        match self {
233            DataContractConfig::V0(v0) => v0.documents_keep_history_contract_default = value,
234            DataContractConfig::V1(v1) => v1.documents_keep_history_contract_default = value,
235        }
236    }
237
238    fn set_documents_can_be_deleted_contract_default(&mut self, value: bool) {
239        match self {
240            DataContractConfig::V0(v0) => v0.documents_can_be_deleted_contract_default = value,
241            DataContractConfig::V1(v1) => v1.documents_can_be_deleted_contract_default = value,
242        }
243    }
244
245    fn set_documents_mutable_contract_default(&mut self, value: bool) {
246        match self {
247            DataContractConfig::V0(v0) => v0.documents_mutable_contract_default = value,
248            DataContractConfig::V1(v1) => v1.documents_mutable_contract_default = value,
249        }
250    }
251
252    fn set_requires_identity_encryption_bounded_key(
253        &mut self,
254        value: Option<StorageKeyRequirements>,
255    ) {
256        match self {
257            DataContractConfig::V0(v0) => v0.requires_identity_encryption_bounded_key = value,
258            DataContractConfig::V1(v1) => v1.requires_identity_encryption_bounded_key = value,
259        }
260    }
261
262    fn set_requires_identity_decryption_bounded_key(
263        &mut self,
264        value: Option<StorageKeyRequirements>,
265    ) {
266        match self {
267            DataContractConfig::V0(v0) => v0.requires_identity_decryption_bounded_key = value,
268            DataContractConfig::V1(v1) => v1.requires_identity_decryption_bounded_key = value,
269        }
270    }
271}
272
273impl DataContractConfigGettersV1 for DataContractConfig {
274    fn sized_integer_types(&self) -> bool {
275        match self {
276            DataContractConfig::V0(_) => false,
277            DataContractConfig::V1(v1) => v1.sized_integer_types,
278        }
279    }
280}
281
282impl DataContractConfigSettersV1 for DataContractConfig {
283    fn set_sized_integer_types_enabled(&mut self, enable: bool) {
284        match self {
285            DataContractConfig::V0(_) => {}
286            DataContractConfig::V1(v1) => v1.sized_integer_types = enable,
287        }
288    }
289}
290
291#[cfg(test)]
292mod tests {
293    use super::*;
294    use crate::data_contract::config::v0::DataContractConfigV0;
295    use crate::data_contract::config::v1::DataContractConfigV1;
296    use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements;
297    use platform_version::version::PlatformVersion;
298
299    mod default_for_version {
300        use super::*;
301
302        #[test]
303        fn default_for_latest_platform_version() {
304            let platform_version = PlatformVersion::latest();
305            let config = DataContractConfig::default_for_version(platform_version)
306                .expect("should create config for latest version");
307
308            // Latest platform version uses contract config V1
309            let expected_version = platform_version
310                .dpp
311                .contract_versions
312                .config
313                .default_current_version;
314
315            assert_eq!(config.version(), expected_version as u16);
316        }
317
318        #[test]
319        fn default_for_first_platform_version() {
320            let platform_version = PlatformVersion::first();
321            let config = DataContractConfig::default_for_version(platform_version)
322                .expect("should create config for first version");
323
324            let expected_version = platform_version
325                .dpp
326                .contract_versions
327                .config
328                .default_current_version;
329
330            assert_eq!(config.version(), expected_version as u16);
331        }
332    }
333
334    mod version_method {
335        use super::*;
336
337        #[test]
338        fn v0_reports_version_0() {
339            let config = DataContractConfig::V0(DataContractConfigV0::default());
340            assert_eq!(config.version(), 0);
341        }
342
343        #[test]
344        fn v1_reports_version_1() {
345            let config = DataContractConfig::V1(DataContractConfigV1::default());
346            assert_eq!(config.version(), 1);
347        }
348    }
349
350    mod from_conversions {
351        use super::*;
352
353        #[test]
354        fn v0_into_config() {
355            let v0 = DataContractConfigV0::default();
356            let config: DataContractConfig = v0.into();
357            assert_eq!(config.version(), 0);
358        }
359
360        #[test]
361        fn v1_into_config() {
362            let v1 = DataContractConfigV1::default();
363            let config: DataContractConfig = v1.into();
364            assert_eq!(config.version(), 1);
365        }
366
367        #[test]
368        fn v1_to_v0_conversion_preserves_fields() {
369            let v1 = DataContractConfigV1 {
370                can_be_deleted: true,
371                readonly: true,
372                keeps_history: true,
373                documents_keep_history_contract_default: true,
374                documents_mutable_contract_default: false,
375                documents_can_be_deleted_contract_default: false,
376                requires_identity_encryption_bounded_key: None,
377                requires_identity_decryption_bounded_key: None,
378                sized_integer_types: true,
379            };
380            let v0: DataContractConfigV0 = v1.into();
381            assert!(v0.can_be_deleted);
382            assert!(v0.readonly);
383            assert!(v0.keeps_history);
384            assert!(v0.documents_keep_history_contract_default);
385            assert!(!v0.documents_mutable_contract_default);
386            assert!(!v0.documents_can_be_deleted_contract_default);
387        }
388    }
389
390    mod getters_v0 {
391        use super::*;
392
393        #[test]
394        fn default_v0_getter_values() {
395            let config = DataContractConfig::V0(DataContractConfigV0::default());
396            assert_eq!(config.can_be_deleted(), DEFAULT_CONTRACT_CAN_BE_DELETED);
397            assert_eq!(config.readonly(), !DEFAULT_CONTRACT_MUTABILITY);
398            assert_eq!(config.keeps_history(), DEFAULT_CONTRACT_KEEPS_HISTORY);
399            assert_eq!(
400                config.documents_keep_history_contract_default(),
401                DEFAULT_CONTRACT_DOCUMENTS_KEEPS_HISTORY
402            );
403            assert_eq!(
404                config.documents_mutable_contract_default(),
405                DEFAULT_CONTRACT_DOCUMENT_MUTABILITY
406            );
407            assert_eq!(
408                config.documents_can_be_deleted_contract_default(),
409                DEFAULT_CONTRACT_DOCUMENTS_CAN_BE_DELETED
410            );
411            assert!(config.requires_identity_encryption_bounded_key().is_none());
412            assert!(config.requires_identity_decryption_bounded_key().is_none());
413        }
414
415        #[test]
416        fn default_v1_getter_values() {
417            let config = DataContractConfig::V1(DataContractConfigV1::default());
418            assert_eq!(config.can_be_deleted(), DEFAULT_CONTRACT_CAN_BE_DELETED);
419            assert_eq!(config.readonly(), !DEFAULT_CONTRACT_MUTABILITY);
420            assert_eq!(config.keeps_history(), DEFAULT_CONTRACT_KEEPS_HISTORY);
421            assert_eq!(
422                config.documents_keep_history_contract_default(),
423                DEFAULT_CONTRACT_DOCUMENTS_KEEPS_HISTORY
424            );
425            assert_eq!(
426                config.documents_mutable_contract_default(),
427                DEFAULT_CONTRACT_DOCUMENT_MUTABILITY
428            );
429            assert_eq!(
430                config.documents_can_be_deleted_contract_default(),
431                DEFAULT_CONTRACT_DOCUMENTS_CAN_BE_DELETED
432            );
433        }
434    }
435
436    mod setters_v0 {
437        use super::*;
438
439        #[test]
440        fn set_can_be_deleted_on_v0() {
441            let mut config = DataContractConfig::V0(DataContractConfigV0::default());
442            config.set_can_be_deleted(true);
443            assert!(config.can_be_deleted());
444            config.set_can_be_deleted(false);
445            assert!(!config.can_be_deleted());
446        }
447
448        #[test]
449        fn set_readonly_on_v1() {
450            let mut config = DataContractConfig::V1(DataContractConfigV1::default());
451            config.set_readonly(true);
452            assert!(config.readonly());
453            config.set_readonly(false);
454            assert!(!config.readonly());
455        }
456
457        #[test]
458        fn set_keeps_history() {
459            let mut config = DataContractConfig::V0(DataContractConfigV0::default());
460            config.set_keeps_history(true);
461            assert!(config.keeps_history());
462        }
463
464        #[test]
465        fn set_documents_keep_history() {
466            let mut config = DataContractConfig::V1(DataContractConfigV1::default());
467            config.set_documents_keep_history_contract_default(true);
468            assert!(config.documents_keep_history_contract_default());
469        }
470
471        #[test]
472        fn set_documents_mutable() {
473            let mut config = DataContractConfig::V0(DataContractConfigV0::default());
474            config.set_documents_mutable_contract_default(false);
475            assert!(!config.documents_mutable_contract_default());
476        }
477
478        #[test]
479        fn set_documents_can_be_deleted() {
480            let mut config = DataContractConfig::V1(DataContractConfigV1::default());
481            config.set_documents_can_be_deleted_contract_default(false);
482            assert!(!config.documents_can_be_deleted_contract_default());
483        }
484
485        #[test]
486        fn set_encryption_key_requirements() {
487            let mut config = DataContractConfig::V0(DataContractConfigV0::default());
488            config
489                .set_requires_identity_encryption_bounded_key(Some(StorageKeyRequirements::Unique));
490            assert_eq!(
491                config.requires_identity_encryption_bounded_key(),
492                Some(StorageKeyRequirements::Unique)
493            );
494        }
495
496        #[test]
497        fn set_decryption_key_requirements() {
498            let mut config = DataContractConfig::V1(DataContractConfigV1::default());
499            config
500                .set_requires_identity_decryption_bounded_key(Some(StorageKeyRequirements::Unique));
501            assert_eq!(
502                config.requires_identity_decryption_bounded_key(),
503                Some(StorageKeyRequirements::Unique)
504            );
505        }
506    }
507
508    mod getters_setters_v1 {
509        use super::*;
510
511        #[test]
512        fn sized_integer_types_default_v1() {
513            let config = DataContractConfig::V1(DataContractConfigV1::default());
514            // V1 defaults to sized_integer_types = true
515            assert!(config.sized_integer_types());
516        }
517
518        #[test]
519        fn sized_integer_types_v0_always_false() {
520            let config = DataContractConfig::V0(DataContractConfigV0::default());
521            assert!(!config.sized_integer_types());
522        }
523
524        #[test]
525        fn set_sized_integer_types_on_v1() {
526            let mut config = DataContractConfig::V1(DataContractConfigV1::default());
527            config.set_sized_integer_types_enabled(false);
528            assert!(!config.sized_integer_types());
529            config.set_sized_integer_types_enabled(true);
530            assert!(config.sized_integer_types());
531        }
532
533        #[test]
534        fn set_sized_integer_types_on_v0_is_noop() {
535            let mut config = DataContractConfig::V0(DataContractConfigV0::default());
536            config.set_sized_integer_types_enabled(true);
537            // V0 does not support sized_integer_types; should remain false
538            assert!(!config.sized_integer_types());
539        }
540    }
541
542    mod config_valid_for_platform_version {
543        use super::*;
544
545        #[test]
546        fn v0_stays_v0_regardless_of_platform() {
547            let config = DataContractConfig::V0(DataContractConfigV0::default());
548            let result = config.config_valid_for_platform_version(PlatformVersion::latest());
549            assert_eq!(result.version(), 0);
550        }
551
552        #[test]
553        fn v1_downgraded_to_v0_when_max_version_is_0() {
554            let config = DataContractConfig::V1(DataContractConfigV1 {
555                can_be_deleted: true,
556                readonly: false,
557                keeps_history: true,
558                documents_keep_history_contract_default: false,
559                documents_mutable_contract_default: true,
560                documents_can_be_deleted_contract_default: true,
561                requires_identity_encryption_bounded_key: None,
562                requires_identity_decryption_bounded_key: None,
563                sized_integer_types: true,
564            });
565
566            // Use first platform version which has config max_version = 0
567            let platform_version = PlatformVersion::first();
568            if platform_version.dpp.contract_versions.config.max_version == 0 {
569                let result = config.config_valid_for_platform_version(platform_version);
570                assert_eq!(result.version(), 0);
571                // The converted V0 should preserve basic fields
572                assert!(result.can_be_deleted());
573            }
574        }
575
576        #[test]
577        fn v1_stays_v1_when_max_version_is_1_or_higher() {
578            let config = DataContractConfig::V1(DataContractConfigV1::default());
579            let platform_version = PlatformVersion::latest();
580            if platform_version.dpp.contract_versions.config.max_version >= 1 {
581                let result = config.config_valid_for_platform_version(platform_version);
582                assert_eq!(result.version(), 1);
583            }
584        }
585    }
586}