1use crate::serialization::{
2 PlatformDeserializableWithBytesLenFromVersionedStructure,
3 PlatformDeserializableWithPotentialValidationFromVersionedStructure,
4 PlatformLimitDeserializableFromVersionedStructure, PlatformSerializableWithPlatformVersion,
5};
6use std::collections::BTreeMap;
7
8use derive_more::From;
9
10use bincode::config::{BigEndian, Configuration};
11use once_cell::sync::Lazy;
12
13pub mod errors;
14pub mod extra;
15
16mod generate_data_contract;
17
18#[cfg(any(feature = "state-transitions", feature = "factories"))]
19pub mod created_data_contract;
20pub mod document_type;
21
22pub mod v0;
23pub mod v1;
24
25#[cfg(feature = "factories")]
26pub mod factory;
27#[cfg(feature = "factories")]
28pub use factory::*;
29#[cfg(any(
30 feature = "value-conversion",
31 feature = "data-contract-cbor-conversion",
32 feature = "json-conversion",
33 feature = "serde-conversion"
34))]
35pub mod conversion;
36#[cfg(feature = "client")]
37mod data_contract_facade;
38#[cfg(feature = "client")]
39pub use data_contract_facade::DataContractFacade;
40mod methods;
41pub mod serialized_version;
42pub use methods::*;
43pub mod accessors;
44pub mod associated_token;
45pub mod change_control_rules;
46pub mod config;
47pub mod group;
48pub mod storage_requirements;
49
50use crate::data_contract::serialized_version::{
51 DataContractInSerializationFormat, CONTRACT_DESERIALIZATION_LIMIT,
52};
53use crate::util::hash::hash_double_to_vec;
54
55use crate::version::{FeatureVersion, PlatformVersion};
56use crate::ProtocolError;
57use crate::ProtocolError::{PlatformDeserializationError, PlatformSerializationError};
58
59pub use crate::data_contract::associated_token::token_configuration::TokenConfiguration;
60use crate::data_contract::group::Group;
61use crate::data_contract::v0::DataContractV0;
62use crate::data_contract::v1::DataContractV1;
63use platform_version::TryIntoPlatformVersioned;
64use platform_versioning::PlatformVersioned;
65pub use serde_json::Value as JsonValue;
66
67type JsonSchema = JsonValue;
68type DefinitionName = String;
69pub type DocumentName = String;
70pub type TokenName = String;
71pub type GroupContractPosition = u16;
72pub type TokenContractPosition = u16;
73pub type DataContractWithSerialization = (DataContract, Vec<u8>);
74type PropertyPath = String;
75
76pub const INITIAL_DATA_CONTRACT_VERSION: u32 = 1;
77
78static EMPTY_GROUPS: Lazy<BTreeMap<GroupContractPosition, Group>> = Lazy::new(BTreeMap::new);
80static EMPTY_TOKENS: Lazy<BTreeMap<TokenContractPosition, TokenConfiguration>> =
81 Lazy::new(BTreeMap::new);
82static EMPTY_KEYWORDS: Lazy<Vec<String>> = Lazy::new(Vec::new);
83
84#[derive(Debug, Clone, PartialEq, From, PlatformVersioned)]
107pub enum DataContract {
108 V0(DataContractV0),
109 V1(DataContractV1),
110}
111
112impl PlatformSerializableWithPlatformVersion for DataContract {
138 type Error = ProtocolError;
139
140 fn serialize_to_bytes_with_platform_version(
141 &self,
142 platform_version: &PlatformVersion,
143 ) -> Result<Vec<u8>, ProtocolError> {
144 let serialization_format: DataContractInSerializationFormat =
145 self.try_into_platform_versioned(platform_version)?;
146 let config = bincode::config::standard()
147 .with_big_endian()
148 .with_no_limit();
149 bincode::encode_to_vec(serialization_format, config).map_err(|e| {
150 PlatformSerializationError(format!("unable to serialize DataContract: {}", e))
151 })
152 }
153
154 fn serialize_consume_to_bytes_with_platform_version(
155 self,
156 platform_version: &PlatformVersion,
157 ) -> Result<Vec<u8>, ProtocolError> {
158 let serialization_format: DataContractInSerializationFormat =
159 self.try_into_platform_versioned(platform_version)?;
160 let config = bincode::config::standard()
161 .with_big_endian()
162 .with_no_limit();
163 bincode::encode_to_vec(serialization_format, config).map_err(|e| {
164 PlatformSerializationError(format!("unable to serialize consume DataContract: {}", e))
165 })
166 }
167}
168
169impl PlatformDeserializableWithPotentialValidationFromVersionedStructure for DataContract {
170 fn versioned_deserialize(
171 data: &[u8],
172 full_validation: bool,
173 platform_version: &PlatformVersion,
174 ) -> Result<Self, ProtocolError>
175 where
176 Self: Sized,
177 {
178 let config = bincode::config::standard()
179 .with_big_endian()
180 .with_no_limit();
181 let data_contract_in_serialization_format: DataContractInSerializationFormat =
182 bincode::borrow_decode_from_slice(data, config)
183 .map_err(|e| {
184 PlatformDeserializationError(format!(
185 "unable to deserialize DataContract: {}",
186 e
187 ))
188 })?
189 .0;
190 DataContract::try_from_platform_versioned(
191 data_contract_in_serialization_format,
192 full_validation,
193 &mut vec![],
194 platform_version,
195 )
196 }
197}
198
199impl PlatformDeserializableWithBytesLenFromVersionedStructure for DataContract {
200 fn versioned_deserialize_with_bytes_len(
201 data: &[u8],
202 full_validation: bool,
203 platform_version: &PlatformVersion,
204 ) -> Result<(Self, usize), ProtocolError>
205 where
206 Self: Sized,
207 {
208 let config = bincode::config::standard()
209 .with_big_endian()
210 .with_no_limit();
211 let (data_contract_in_serialization_format, len) = bincode::borrow_decode_from_slice::<
212 DataContractInSerializationFormat,
213 Configuration<BigEndian>,
214 >(data, config)
215 .map_err(|e| {
216 PlatformDeserializationError(format!("unable to deserialize DataContract: {}", e))
217 })?;
218 Ok((
219 DataContract::try_from_platform_versioned(
220 data_contract_in_serialization_format,
221 full_validation,
222 &mut vec![],
223 platform_version,
224 )?,
225 len,
226 ))
227 }
228}
229
230impl PlatformLimitDeserializableFromVersionedStructure for DataContract {
231 fn versioned_limit_deserialize(
232 data: &[u8],
233 platform_version: &PlatformVersion,
234 ) -> Result<Self, ProtocolError>
235 where
236 Self: Sized,
237 {
238 let config = bincode::config::standard()
239 .with_big_endian()
240 .with_limit::<CONTRACT_DESERIALIZATION_LIMIT>();
241 let data_contract_in_serialization_format: DataContractInSerializationFormat =
242 bincode::borrow_decode_from_slice(data, config)
243 .map_err(|e| {
244 PlatformDeserializationError(format!(
245 "unable to deserialize DataContract with limit: {}",
246 e
247 ))
248 })?
249 .0;
250 DataContract::try_from_platform_versioned(
252 data_contract_in_serialization_format,
253 true,
254 &mut vec![],
255 platform_version,
256 )
257 }
258}
259
260impl DataContract {
261 pub fn as_v0(&self) -> Option<&DataContractV0> {
262 match self {
263 DataContract::V0(v0) => Some(v0),
264 _ => None,
265 }
266 }
267
268 pub fn as_v0_mut(&mut self) -> Option<&mut DataContractV0> {
269 match self {
270 DataContract::V0(v0) => Some(v0),
271 _ => None,
272 }
273 }
274
275 pub fn into_v0(self) -> Option<DataContractV0> {
276 match self {
277 DataContract::V0(v0) => Some(v0),
278 _ => None,
279 }
280 }
281
282 pub fn as_v1(&self) -> Option<&DataContractV1> {
283 match self {
284 DataContract::V1(v1) => Some(v1),
285 _ => None,
286 }
287 }
288
289 pub fn as_v1_mut(&mut self) -> Option<&mut DataContractV1> {
290 match self {
291 DataContract::V1(v1) => Some(v1),
292 _ => None,
293 }
294 }
295
296 pub fn into_v1(self) -> Option<DataContractV1> {
297 match self {
298 DataContract::V1(v1) => Some(v1),
299 _ => None,
300 }
301 }
302
303 #[cfg(test)]
305 pub fn into_latest(self) -> Option<DataContractV1> {
306 self.into_v1()
307 }
308
309 #[cfg(test)]
311 pub fn as_latest(&self) -> Option<&DataContractV1> {
312 match self {
313 DataContract::V1(v1) => Some(v1),
314 _ => None,
315 }
316 }
317
318 #[cfg(test)]
320 pub fn as_latest_mut(&mut self) -> Option<&mut DataContractV1> {
321 match self {
322 DataContract::V1(v1) => Some(v1),
323 _ => None,
324 }
325 }
326
327 pub fn check_version_is_active(
328 protocol_version: u32,
329 data_contract_system_version: FeatureVersion,
330 ) -> Result<bool, ProtocolError> {
331 let platform_version = PlatformVersion::get(protocol_version)?;
332 Ok(platform_version
333 .dpp
334 .contract_versions
335 .contract_structure_version
336 == data_contract_system_version)
337 }
338
339 pub fn hash(&self, platform_version: &PlatformVersion) -> Result<Vec<u8>, ProtocolError> {
340 Ok(hash_double_to_vec(
341 self.serialize_to_bytes_with_platform_version(platform_version)?,
342 ))
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use crate::data_contract::accessors::v0::DataContractV0Getters;
349 use crate::data_contract::config::v0::DataContractConfigGettersV0;
350 use crate::data_contract::document_type::accessors::DocumentTypeV0Getters;
351 use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements;
352 use crate::data_contract::DataContract;
353 use crate::serialization::PlatformDeserializableWithPotentialValidationFromVersionedStructure;
354 use crate::serialization::PlatformSerializableWithPlatformVersion;
355 use crate::system_data_contracts::load_system_data_contract;
356 use crate::tests::fixtures::{
357 get_dashpay_contract_fixture, get_dashpay_contract_with_generalized_encryption_key_fixture,
358 };
359 use crate::version::PlatformVersion;
360 use data_contracts::SystemDataContract::Dashpay;
361
362 #[test]
363 fn test_contract_serialization() {
364 let platform_version = PlatformVersion::latest();
365 let data_contract = load_system_data_contract(Dashpay, platform_version)
366 .expect("expected dashpay contract");
367 let serialized = data_contract
368 .serialize_to_bytes_with_platform_version(platform_version)
369 .expect("expected to serialize data contract");
370 assert_eq!(
371 serialized[0],
372 platform_version
373 .dpp
374 .contract_versions
375 .contract_serialization_version
376 .default_current_version as u8
377 );
378
379 let unserialized = DataContract::versioned_deserialize(&serialized, true, platform_version)
380 .expect("expected to deserialize data contract");
381
382 assert_eq!(data_contract, unserialized);
383 }
384
385 #[test]
386 fn test_contract_can_have_specialized_contract_encryption_decryption_keys() {
387 let data_contract =
388 get_dashpay_contract_with_generalized_encryption_key_fixture(None, 0, 1)
389 .data_contract_owned();
390 assert_eq!(
391 data_contract
392 .config()
393 .requires_identity_decryption_bounded_key(),
394 Some(StorageKeyRequirements::Unique)
395 );
396 assert_eq!(
397 data_contract
398 .config()
399 .requires_identity_encryption_bounded_key(),
400 Some(StorageKeyRequirements::Unique)
401 );
402 }
403
404 #[test]
405 fn test_contract_document_type_can_have_specialized_contract_encryption_decryption_keys() {
406 let data_contract = get_dashpay_contract_fixture(None, 0, 1).data_contract_owned();
407 assert_eq!(
408 data_contract
409 .document_type_for_name("contactRequest")
410 .expect("expected document type")
411 .requires_identity_decryption_bounded_key(),
412 Some(StorageKeyRequirements::MultipleReferenceToLatest)
413 );
414 assert_eq!(
415 data_contract
416 .document_type_for_name("contactRequest")
417 .expect("expected document type")
418 .requires_identity_encryption_bounded_key(),
419 Some(StorageKeyRequirements::MultipleReferenceToLatest)
420 );
421 }
422}