Skip to main content

dpp/data_contract/created_data_contract/
mod.rs

1mod fields;
2pub mod v0;
3
4use crate::data_contract::created_data_contract::v0::{
5    CreatedDataContractInSerializationFormatV0, CreatedDataContractV0,
6};
7use crate::prelude::{DataContract, IdentityNonce};
8use crate::version::PlatformVersion;
9use crate::ProtocolError;
10use bincode::{Decode, DecodeUntrusted, Encode};
11use derive_more::From;
12
13use crate::data_contract::serialized_version::DataContractInSerializationFormat;
14use crate::serialization::{
15    PlatformDeserializableWithPotentialValidationFromVersionedStructureTrusted,
16    PlatformDeserializableWithPotentialValidationFromVersionedStructureUntrusted,
17    PlatformSerializableWithPlatformVersion,
18};
19use crate::ProtocolError::{PlatformDeserializationError, PlatformSerializationError};
20use platform_serialization::PLATFORM_BINCODE_CONFIG;
21use platform_version::TryIntoPlatformVersioned;
22
23/// The created data contract is a intermediate structure that can be consumed by a
24/// contract create state transition.
25///
26///
27
28#[derive(Clone, Debug, PartialEq, From)]
29pub enum CreatedDataContract {
30    V0(CreatedDataContractV0),
31}
32
33#[derive(Clone, Debug, Encode, Decode, From, DecodeUntrusted)]
34pub enum CreatedDataContractInSerializationFormat {
35    V0(CreatedDataContractInSerializationFormatV0),
36}
37
38impl PlatformSerializableWithPlatformVersion for CreatedDataContract {
39    type Error = ProtocolError;
40
41    fn serialize_to_bytes_with_platform_version(
42        &self,
43        platform_version: &PlatformVersion,
44    ) -> Result<Vec<u8>, ProtocolError> {
45        self.clone()
46            .serialize_consume_to_bytes_with_platform_version(platform_version)
47    }
48
49    fn serialize_consume_to_bytes_with_platform_version(
50        self,
51        platform_version: &PlatformVersion,
52    ) -> Result<Vec<u8>, ProtocolError> {
53        let (data_contract, identity_nonce) = self.data_contract_and_identity_nonce();
54        let data_contract_serialization_format: DataContractInSerializationFormat =
55            data_contract.try_into_platform_versioned(platform_version)?;
56        let created_data_contract_in_serialization_format = match platform_version
57            .dpp
58            .contract_versions
59            .created_data_contract_structure
60        {
61            0 => Ok(CreatedDataContractInSerializationFormat::V0(
62                CreatedDataContractInSerializationFormatV0 {
63                    data_contract: data_contract_serialization_format,
64                    identity_nonce,
65                },
66            )),
67            version => Err(ProtocolError::UnknownVersionMismatch {
68                method: "CreatedDataContract::serialize_to_bytes_with_platform_version".to_string(),
69                known_versions: vec![0],
70                received: version,
71            }),
72        }?;
73        let config = PLATFORM_BINCODE_CONFIG;
74        bincode::encode_to_vec(created_data_contract_in_serialization_format, config).map_err(|e| {
75            PlatformSerializationError(format!("unable to serialize CreatedDataContract: {}", e))
76        })
77    }
78}
79
80impl CreatedDataContract {
81    /// Rebuilds the contract from its decoded serialization format; the
82    /// trusted and untrusted entry points differ only in the decoder that
83    /// produced `created_data_contract_in_serialization_format`.
84    fn from_serialization_format(
85        created_data_contract_in_serialization_format: CreatedDataContractInSerializationFormat,
86        full_validation: bool,
87        platform_version: &PlatformVersion,
88        method: &str,
89    ) -> Result<Self, ProtocolError> {
90        let (data_contract_in_serialization_format, identity_nonce) =
91            created_data_contract_in_serialization_format.data_contract_and_identity_nonce_owned();
92        // A contract about to be registered is read from its schemas, whatever its format
93        let data_contract = DataContract::try_from_platform_versioned(
94            data_contract_in_serialization_format.into_schema_form(),
95            full_validation,
96            &mut vec![],
97            platform_version,
98        )?;
99        match platform_version
100            .dpp
101            .contract_versions
102            .created_data_contract_structure
103        {
104            0 => Ok(CreatedDataContract::V0(CreatedDataContractV0 {
105                data_contract,
106                identity_nonce,
107            })),
108            version => Err(ProtocolError::UnknownVersionMismatch {
109                method: method.to_string(),
110                known_versions: vec![0],
111                received: version,
112            }),
113        }
114    }
115}
116
117impl PlatformDeserializableWithPotentialValidationFromVersionedStructureTrusted
118    for CreatedDataContract
119{
120    fn versioned_deserialize_trusted(
121        data: &[u8],
122        full_validation: bool,
123        platform_version: &PlatformVersion,
124    ) -> Result<Self, ProtocolError>
125    where
126        Self: Sized,
127    {
128        let config = PLATFORM_BINCODE_CONFIG;
129        let created_data_contract_in_serialization_format: CreatedDataContractInSerializationFormat =
130            bincode::borrow_decode_from_slice(data, config)
131                .map_err(|e| {
132                    PlatformDeserializationError(format!(
133                        "unable to deserialize DataContract: {}",
134                        e
135                    ))
136                })?
137                .0;
138        Self::from_serialization_format(
139            created_data_contract_in_serialization_format,
140            full_validation,
141            platform_version,
142            "CreatedDataContract::versioned_deserialize_trusted",
143        )
144    }
145}
146
147impl PlatformDeserializableWithPotentialValidationFromVersionedStructureUntrusted
148    for CreatedDataContract
149{
150    fn versioned_deserialize_untrusted(
151        data: &[u8],
152        full_validation: bool,
153        platform_version: &PlatformVersion,
154    ) -> Result<Self, ProtocolError>
155    where
156        Self: Sized,
157    {
158        let config = PLATFORM_BINCODE_CONFIG;
159        let created_data_contract_in_serialization_format: CreatedDataContractInSerializationFormat =
160            bincode::borrow_decode_from_slice_untrusted(data, config)
161                .map_err(|e| {
162                    PlatformDeserializationError(format!(
163                        "unable to deserialize DataContract: {}",
164                        e
165                    ))
166                })?
167                .0;
168        Self::from_serialization_format(
169            created_data_contract_in_serialization_format,
170            full_validation,
171            platform_version,
172            "CreatedDataContract::versioned_deserialize_untrusted",
173        )
174    }
175}
176
177impl From<CreatedDataContract> for DataContract {
178    fn from(value: CreatedDataContract) -> Self {
179        match value {
180            CreatedDataContract::V0(created_data_contract) => created_data_contract.data_contract,
181        }
182    }
183}
184
185impl CreatedDataContract {
186    pub fn data_contract_owned(self) -> DataContract {
187        match self {
188            CreatedDataContract::V0(v0) => v0.data_contract,
189        }
190    }
191
192    pub fn data_contract_and_identity_nonce(self) -> (DataContract, IdentityNonce) {
193        match self {
194            CreatedDataContract::V0(v0) => (v0.data_contract, v0.identity_nonce),
195        }
196    }
197
198    pub fn data_contract(&self) -> &DataContract {
199        match self {
200            CreatedDataContract::V0(v0) => &v0.data_contract,
201        }
202    }
203
204    pub fn data_contract_mut(&mut self) -> &mut DataContract {
205        match self {
206            CreatedDataContract::V0(v0) => &mut v0.data_contract,
207        }
208    }
209
210    pub fn identity_nonce(&self) -> IdentityNonce {
211        match self {
212            CreatedDataContract::V0(v0) => v0.identity_nonce,
213        }
214    }
215
216    #[cfg(test)]
217    pub fn set_identity_nonce(&mut self, identity_nonce: IdentityNonce) {
218        match self {
219            CreatedDataContract::V0(v0) => v0.identity_nonce = identity_nonce,
220        }
221    }
222
223    pub fn from_contract_and_identity_nonce(
224        data_contract: DataContract,
225        identity_nonce: IdentityNonce,
226        platform_version: &PlatformVersion,
227    ) -> Result<CreatedDataContract, ProtocolError> {
228        match platform_version
229            .dpp
230            .contract_versions
231            .created_data_contract_structure
232        {
233            0 => Ok(CreatedDataContractV0 {
234                data_contract,
235                identity_nonce,
236            }
237            .into()),
238            version => Err(ProtocolError::UnknownVersionMismatch {
239                method: "CreatedDataContract::from_contract_and_entropy".to_string(),
240                known_versions: vec![0],
241                received: version,
242            }),
243        }
244    }
245}
246
247impl CreatedDataContractInSerializationFormat {
248    pub fn data_contract_and_identity_nonce_owned(
249        self,
250    ) -> (DataContractInSerializationFormat, IdentityNonce) {
251        match self {
252            CreatedDataContractInSerializationFormat::V0(v0) => {
253                (v0.data_contract, v0.identity_nonce)
254            }
255        }
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262    use crate::data_contract::accessors::v0::{DataContractV0Getters, DataContractV0Setters};
263    use crate::tests::fixtures::get_data_contract_fixture;
264    use crate::version::PlatformVersion;
265    use platform_serialization::PLATFORM_BINCODE_CONFIG;
266
267    fn sample_created() -> CreatedDataContract {
268        get_data_contract_fixture(None, 42, 1)
269    }
270
271    // -----------------------------------------------------------------------
272    // Constructor / getter coverage
273    // -----------------------------------------------------------------------
274
275    #[test]
276    fn from_contract_and_identity_nonce_wraps_v0() {
277        let platform_version = PlatformVersion::latest();
278        let created = sample_created();
279        let contract = created.data_contract().clone();
280        let wrapped = CreatedDataContract::from_contract_and_identity_nonce(
281            contract.clone(),
282            99,
283            platform_version,
284        )
285        .expect("should wrap successfully on latest version");
286        assert_eq!(wrapped.identity_nonce(), 99);
287        assert_eq!(wrapped.data_contract().id(), contract.id());
288        // from_contract_and_identity_nonce always produces the V0 variant today.
289        assert!(matches!(wrapped, CreatedDataContract::V0(_)));
290    }
291
292    #[test]
293    fn identity_nonce_getter_returns_underlying_value() {
294        let created = sample_created();
295        assert_eq!(created.identity_nonce(), 42);
296    }
297
298    #[test]
299    fn data_contract_getter_returns_same_id() {
300        let created = sample_created();
301        let via_getter = created.data_contract();
302        let expected_id = via_getter.id();
303        // Also verify the owned getter yields the same id.
304        let owned = created.clone().data_contract_owned();
305        assert_eq!(owned.id(), expected_id);
306    }
307
308    #[test]
309    fn data_contract_mut_allows_mutation() {
310        let mut created = sample_created();
311        let original_id = created.data_contract().id();
312        let new_id = platform_value::Identifier::from([7u8; 32]);
313        // Mutate via the mutable accessor to prove it hands out a real mut ref.
314        created.data_contract_mut().set_id(new_id);
315        assert_ne!(created.data_contract().id(), original_id);
316        assert_eq!(created.data_contract().id(), new_id);
317    }
318
319    #[test]
320    fn data_contract_and_identity_nonce_extracts_both() {
321        let created = sample_created();
322        let (contract, nonce) = created.clone().data_contract_and_identity_nonce();
323        assert_eq!(nonce, 42);
324        assert_eq!(contract.id(), created.data_contract().id());
325    }
326
327    #[test]
328    fn set_identity_nonce_updates_value() {
329        let mut created = sample_created();
330        created.set_identity_nonce(777);
331        assert_eq!(created.identity_nonce(), 777);
332    }
333
334    // -----------------------------------------------------------------------
335    // From<CreatedDataContract> for DataContract
336    // -----------------------------------------------------------------------
337
338    #[test]
339    fn from_created_to_data_contract() {
340        let created = sample_created();
341        let expected_id = created.data_contract().id();
342        let dc: DataContract = created.into();
343        assert_eq!(dc.id(), expected_id);
344    }
345
346    // -----------------------------------------------------------------------
347    // Bincode serialize / deserialize round-trip via
348    // PlatformSerializableWithPlatformVersion +
349    // PlatformDeserializableWithPotentialValidationFromVersionedStructureUntrusted.
350    // -----------------------------------------------------------------------
351
352    #[test]
353    fn serialize_roundtrip_via_platform_version() {
354        let platform_version = PlatformVersion::latest();
355        let created = sample_created();
356        let bytes = created
357            .serialize_to_bytes_with_platform_version(platform_version)
358            .expect("serialize should succeed");
359        assert!(!bytes.is_empty());
360
361        let restored =
362            CreatedDataContract::versioned_deserialize_untrusted(&bytes, false, platform_version)
363                .expect("deserialize should succeed");
364        assert_eq!(restored.identity_nonce(), created.identity_nonce());
365        assert_eq!(restored.data_contract().id(), created.data_contract().id());
366    }
367
368    #[test]
369    fn serialize_consume_to_bytes_matches_clone_path() {
370        let platform_version = PlatformVersion::latest();
371        let created = sample_created();
372        let via_ref = created
373            .serialize_to_bytes_with_platform_version(platform_version)
374            .expect("ref serialize should succeed");
375        let via_consume = created
376            .clone()
377            .serialize_consume_to_bytes_with_platform_version(platform_version)
378            .expect("consume serialize should succeed");
379        // The ref path internally clones and delegates to the consume path;
380        // both must produce byte-identical output.
381        assert_eq!(via_ref, via_consume);
382    }
383
384    #[test]
385    fn versioned_deserialize_rejects_garbage() {
386        let platform_version = PlatformVersion::latest();
387        let garbage = vec![0xFFu8; 16];
388        let err =
389            CreatedDataContract::versioned_deserialize_untrusted(&garbage, false, platform_version)
390                .expect_err("random bytes should not deserialize");
391        match err {
392            ProtocolError::PlatformDeserializationError(_) => {}
393            other => panic!("expected PlatformDeserializationError, got {other:?}"),
394        }
395    }
396
397    #[test]
398    fn versioned_deserialize_rejects_empty_input() {
399        let platform_version = PlatformVersion::latest();
400        let err =
401            CreatedDataContract::versioned_deserialize_untrusted(&[], false, platform_version)
402                .expect_err("empty input should not deserialize");
403        assert!(matches!(
404            err,
405            ProtocolError::PlatformDeserializationError(_)
406        ));
407    }
408
409    #[test]
410    fn versioned_deserialize_trusted_agrees_with_untrusted() {
411        let platform_version = PlatformVersion::latest();
412        let created = sample_created();
413        let bytes = created
414            .serialize_to_bytes_with_platform_version(platform_version)
415            .expect("serialize should succeed");
416
417        let trusted =
418            CreatedDataContract::versioned_deserialize_trusted(&bytes, false, platform_version)
419                .expect("trusted deserialize should succeed");
420        let untrusted =
421            CreatedDataContract::versioned_deserialize_untrusted(&bytes, false, platform_version)
422                .expect("untrusted deserialize should succeed");
423        assert_eq!(trusted, untrusted);
424        assert_eq!(trusted.identity_nonce(), created.identity_nonce());
425        assert_eq!(trusted.data_contract().id(), created.data_contract().id());
426    }
427
428    #[test]
429    fn versioned_deserialize_trusted_rejects_garbage_and_empty_input() {
430        let platform_version = PlatformVersion::latest();
431        for input in [vec![0xFFu8; 16], vec![]] {
432            let err =
433                CreatedDataContract::versioned_deserialize_trusted(&input, false, platform_version)
434                    .expect_err("malformed input should not deserialize");
435            assert!(matches!(
436                err,
437                ProtocolError::PlatformDeserializationError(_)
438            ));
439        }
440    }
441
442    // -----------------------------------------------------------------------
443    // CreatedDataContractInSerializationFormat helpers
444    // -----------------------------------------------------------------------
445
446    #[test]
447    fn in_serialization_format_data_contract_and_identity_nonce_owned() {
448        let platform_version = PlatformVersion::latest();
449        let created = sample_created();
450        // Re-serialize and re-decode to obtain a raw in-serialization-format value
451        // without reaching into private fields.
452        let bytes = created
453            .clone()
454            .serialize_consume_to_bytes_with_platform_version(platform_version)
455            .expect("serialize");
456        let config = PLATFORM_BINCODE_CONFIG;
457        let (decoded, _consumed) = bincode::borrow_decode_from_slice_untrusted::<
458            CreatedDataContractInSerializationFormat,
459            _,
460        >(&bytes, config)
461        .expect("raw bincode decode should succeed");
462        let (_contract_fmt, nonce) = decoded.data_contract_and_identity_nonce_owned();
463        assert_eq!(nonce, created.identity_nonce());
464    }
465
466    // -----------------------------------------------------------------------
467    // Clone / PartialEq smoke — these derives are real (not generated per
468    // variant) and are used elsewhere in the codebase via matches on equality.
469    // -----------------------------------------------------------------------
470
471    #[test]
472    fn clone_and_equality() {
473        let a = sample_created();
474        let b = a.clone();
475        assert_eq!(a, b);
476    }
477
478    #[test]
479    fn different_nonce_breaks_equality() {
480        let a = sample_created();
481        let mut b = a.clone();
482        b.set_identity_nonce(a.identity_nonce().wrapping_add(1));
483        assert_ne!(a, b);
484    }
485}