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, Encode};
11use derive_more::From;
12
13use crate::data_contract::serialized_version::DataContractInSerializationFormat;
14use crate::serialization::{
15    PlatformDeserializableWithPotentialValidationFromVersionedStructure,
16    PlatformSerializableWithPlatformVersion,
17};
18use crate::ProtocolError::{PlatformDeserializationError, PlatformSerializationError};
19#[cfg(feature = "value-conversion")]
20use platform_value::Value;
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)]
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 = bincode::config::standard()
74            .with_big_endian()
75            .with_no_limit();
76        bincode::encode_to_vec(created_data_contract_in_serialization_format, config).map_err(|e| {
77            PlatformSerializationError(format!("unable to serialize CreatedDataContract: {}", e))
78        })
79    }
80}
81
82impl PlatformDeserializableWithPotentialValidationFromVersionedStructure for CreatedDataContract {
83    fn versioned_deserialize(
84        data: &[u8],
85        full_validation: bool,
86        platform_version: &PlatformVersion,
87    ) -> Result<Self, ProtocolError>
88    where
89        Self: Sized,
90    {
91        let config = bincode::config::standard()
92            .with_big_endian()
93            .with_no_limit();
94        let created_data_contract_in_serialization_format: CreatedDataContractInSerializationFormat =
95            bincode::borrow_decode_from_slice(data, config)
96                .map_err(|e| {
97                    PlatformDeserializationError(format!(
98                        "unable to deserialize DataContract: {}",
99                        e
100                    ))
101                })?
102                .0;
103        let (data_contract_in_serialization_format, identity_nonce) =
104            created_data_contract_in_serialization_format.data_contract_and_identity_nonce_owned();
105        let data_contract = DataContract::try_from_platform_versioned(
106            data_contract_in_serialization_format,
107            full_validation,
108            &mut vec![],
109            platform_version,
110        )?;
111        match platform_version
112            .dpp
113            .contract_versions
114            .created_data_contract_structure
115        {
116            0 => Ok(CreatedDataContract::V0(CreatedDataContractV0 {
117                data_contract,
118                identity_nonce,
119            })),
120            version => Err(ProtocolError::UnknownVersionMismatch {
121                method: "CreatedDataContract::versioned_deserialize".to_string(),
122                known_versions: vec![0],
123                received: version,
124            }),
125        }
126    }
127}
128
129impl From<CreatedDataContract> for DataContract {
130    fn from(value: CreatedDataContract) -> Self {
131        match value {
132            CreatedDataContract::V0(created_data_contract) => created_data_contract.data_contract,
133        }
134    }
135}
136
137impl CreatedDataContract {
138    pub fn data_contract_owned(self) -> DataContract {
139        match self {
140            CreatedDataContract::V0(v0) => v0.data_contract,
141        }
142    }
143
144    pub fn data_contract_and_identity_nonce(self) -> (DataContract, IdentityNonce) {
145        match self {
146            CreatedDataContract::V0(v0) => (v0.data_contract, v0.identity_nonce),
147        }
148    }
149
150    pub fn data_contract(&self) -> &DataContract {
151        match self {
152            CreatedDataContract::V0(v0) => &v0.data_contract,
153        }
154    }
155
156    pub fn data_contract_mut(&mut self) -> &mut DataContract {
157        match self {
158            CreatedDataContract::V0(v0) => &mut v0.data_contract,
159        }
160    }
161
162    pub fn identity_nonce(&self) -> IdentityNonce {
163        match self {
164            CreatedDataContract::V0(v0) => v0.identity_nonce,
165        }
166    }
167
168    #[cfg(test)]
169    pub fn set_identity_nonce(&mut self, identity_nonce: IdentityNonce) {
170        match self {
171            CreatedDataContract::V0(v0) => v0.identity_nonce = identity_nonce,
172        }
173    }
174
175    pub fn from_contract_and_identity_nonce(
176        data_contract: DataContract,
177        identity_nonce: IdentityNonce,
178        platform_version: &PlatformVersion,
179    ) -> Result<CreatedDataContract, ProtocolError> {
180        match platform_version
181            .dpp
182            .contract_versions
183            .created_data_contract_structure
184        {
185            0 => Ok(CreatedDataContractV0 {
186                data_contract,
187                identity_nonce,
188            }
189            .into()),
190            version => Err(ProtocolError::UnknownVersionMismatch {
191                method: "CreatedDataContract::from_contract_and_entropy".to_string(),
192                known_versions: vec![0],
193                received: version,
194            }),
195        }
196    }
197
198    #[cfg(feature = "value-conversion")]
199    pub fn from_object(
200        raw_object: Value,
201        full_validation: bool,
202        platform_version: &PlatformVersion,
203    ) -> Result<Self, ProtocolError> {
204        match platform_version
205            .dpp
206            .contract_versions
207            .created_data_contract_structure
208        {
209            0 => Ok(CreatedDataContractV0::from_object(
210                raw_object,
211                full_validation,
212                platform_version,
213            )?
214            .into()),
215            version => Err(ProtocolError::UnknownVersionMismatch {
216                method: "CreatedDataContract::from_object".to_string(),
217                known_versions: vec![0],
218                received: version,
219            }),
220        }
221    }
222}
223
224impl CreatedDataContractInSerializationFormat {
225    pub fn data_contract_and_identity_nonce_owned(
226        self,
227    ) -> (DataContractInSerializationFormat, IdentityNonce) {
228        match self {
229            CreatedDataContractInSerializationFormat::V0(v0) => {
230                (v0.data_contract, v0.identity_nonce)
231            }
232        }
233    }
234}
235
236#[cfg(test)]
237mod tests {
238    use super::*;
239    use crate::data_contract::accessors::v0::{DataContractV0Getters, DataContractV0Setters};
240    use crate::tests::fixtures::get_data_contract_fixture;
241    use crate::version::PlatformVersion;
242
243    fn sample_created() -> CreatedDataContract {
244        get_data_contract_fixture(None, 42, 1)
245    }
246
247    // -----------------------------------------------------------------------
248    // Constructor / getter coverage
249    // -----------------------------------------------------------------------
250
251    #[test]
252    fn from_contract_and_identity_nonce_wraps_v0() {
253        let platform_version = PlatformVersion::latest();
254        let created = sample_created();
255        let contract = created.data_contract().clone();
256        let wrapped = CreatedDataContract::from_contract_and_identity_nonce(
257            contract.clone(),
258            99,
259            platform_version,
260        )
261        .expect("should wrap successfully on latest version");
262        assert_eq!(wrapped.identity_nonce(), 99);
263        assert_eq!(wrapped.data_contract().id(), contract.id());
264        // from_contract_and_identity_nonce always produces the V0 variant today.
265        assert!(matches!(wrapped, CreatedDataContract::V0(_)));
266    }
267
268    #[test]
269    fn identity_nonce_getter_returns_underlying_value() {
270        let created = sample_created();
271        assert_eq!(created.identity_nonce(), 42);
272    }
273
274    #[test]
275    fn data_contract_getter_returns_same_id() {
276        let created = sample_created();
277        let via_getter = created.data_contract();
278        let expected_id = via_getter.id();
279        // Also verify the owned getter yields the same id.
280        let owned = created.clone().data_contract_owned();
281        assert_eq!(owned.id(), expected_id);
282    }
283
284    #[test]
285    fn data_contract_mut_allows_mutation() {
286        let mut created = sample_created();
287        let original_id = created.data_contract().id();
288        let new_id = platform_value::Identifier::from([7u8; 32]);
289        // Mutate via the mutable accessor to prove it hands out a real mut ref.
290        created.data_contract_mut().set_id(new_id);
291        assert_ne!(created.data_contract().id(), original_id);
292        assert_eq!(created.data_contract().id(), new_id);
293    }
294
295    #[test]
296    fn data_contract_and_identity_nonce_extracts_both() {
297        let created = sample_created();
298        let (contract, nonce) = created.clone().data_contract_and_identity_nonce();
299        assert_eq!(nonce, 42);
300        assert_eq!(contract.id(), created.data_contract().id());
301    }
302
303    #[test]
304    fn set_identity_nonce_updates_value() {
305        let mut created = sample_created();
306        created.set_identity_nonce(777);
307        assert_eq!(created.identity_nonce(), 777);
308    }
309
310    // -----------------------------------------------------------------------
311    // From<CreatedDataContract> for DataContract
312    // -----------------------------------------------------------------------
313
314    #[test]
315    fn from_created_to_data_contract() {
316        let created = sample_created();
317        let expected_id = created.data_contract().id();
318        let dc: DataContract = created.into();
319        assert_eq!(dc.id(), expected_id);
320    }
321
322    // -----------------------------------------------------------------------
323    // Bincode serialize / deserialize round-trip via
324    // PlatformSerializableWithPlatformVersion +
325    // PlatformDeserializableWithPotentialValidationFromVersionedStructure.
326    // -----------------------------------------------------------------------
327
328    #[test]
329    fn serialize_roundtrip_via_platform_version() {
330        let platform_version = PlatformVersion::latest();
331        let created = sample_created();
332        let bytes = created
333            .serialize_to_bytes_with_platform_version(platform_version)
334            .expect("serialize should succeed");
335        assert!(!bytes.is_empty());
336
337        let restored = CreatedDataContract::versioned_deserialize(&bytes, false, platform_version)
338            .expect("deserialize should succeed");
339        assert_eq!(restored.identity_nonce(), created.identity_nonce());
340        assert_eq!(restored.data_contract().id(), created.data_contract().id());
341    }
342
343    #[test]
344    fn serialize_consume_to_bytes_matches_clone_path() {
345        let platform_version = PlatformVersion::latest();
346        let created = sample_created();
347        let via_ref = created
348            .serialize_to_bytes_with_platform_version(platform_version)
349            .expect("ref serialize should succeed");
350        let via_consume = created
351            .clone()
352            .serialize_consume_to_bytes_with_platform_version(platform_version)
353            .expect("consume serialize should succeed");
354        // The ref path internally clones and delegates to the consume path;
355        // both must produce byte-identical output.
356        assert_eq!(via_ref, via_consume);
357    }
358
359    #[test]
360    fn versioned_deserialize_rejects_garbage() {
361        let platform_version = PlatformVersion::latest();
362        let garbage = vec![0xFFu8; 16];
363        let err = CreatedDataContract::versioned_deserialize(&garbage, false, platform_version)
364            .expect_err("random bytes should not deserialize");
365        match err {
366            ProtocolError::PlatformDeserializationError(_) => {}
367            other => panic!("expected PlatformDeserializationError, got {other:?}"),
368        }
369    }
370
371    #[test]
372    fn versioned_deserialize_rejects_empty_input() {
373        let platform_version = PlatformVersion::latest();
374        let err = CreatedDataContract::versioned_deserialize(&[], false, platform_version)
375            .expect_err("empty input should not deserialize");
376        assert!(matches!(
377            err,
378            ProtocolError::PlatformDeserializationError(_)
379        ));
380    }
381
382    // -----------------------------------------------------------------------
383    // CreatedDataContractInSerializationFormat helpers
384    // -----------------------------------------------------------------------
385
386    #[test]
387    fn in_serialization_format_data_contract_and_identity_nonce_owned() {
388        let platform_version = PlatformVersion::latest();
389        let created = sample_created();
390        // Re-serialize and re-decode to obtain a raw in-serialization-format value
391        // without reaching into private fields.
392        let bytes = created
393            .clone()
394            .serialize_consume_to_bytes_with_platform_version(platform_version)
395            .expect("serialize");
396        let config = bincode::config::standard()
397            .with_big_endian()
398            .with_no_limit();
399        let (decoded, _consumed) = bincode::borrow_decode_from_slice::<
400            CreatedDataContractInSerializationFormat,
401            _,
402        >(&bytes, config)
403        .expect("raw bincode decode should succeed");
404        let (_contract_fmt, nonce) = decoded.data_contract_and_identity_nonce_owned();
405        assert_eq!(nonce, created.identity_nonce());
406    }
407
408    // -----------------------------------------------------------------------
409    // Clone / PartialEq smoke — these derives are real (not generated per
410    // variant) and are used elsewhere in the codebase via matches on equality.
411    // -----------------------------------------------------------------------
412
413    #[test]
414    fn clone_and_equality() {
415        let a = sample_created();
416        let b = a.clone();
417        assert_eq!(a, b);
418    }
419
420    #[test]
421    fn different_nonce_breaks_equality() {
422        let a = sample_created();
423        let mut b = a.clone();
424        b.set_identity_nonce(a.identity_nonce().wrapping_add(1));
425        assert_ne!(a, b);
426    }
427}