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