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