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 {
133 type Error = ProtocolError;
134
135 fn serialize_to_bytes_with_platform_version(
136 &self,
137 platform_version: &PlatformVersion,
138 ) -> Result<Vec<u8>, ProtocolError> {
139 let serialization_format: DataContractInSerializationFormat =
140 self.try_into_platform_versioned(platform_version)?;
141 let config = bincode::config::standard()
142 .with_big_endian()
143 .with_no_limit();
144 bincode::encode_to_vec(serialization_format, config).map_err(|e| {
145 PlatformSerializationError(format!("unable to serialize DataContract: {}", e))
146 })
147 }
148
149 fn serialize_consume_to_bytes_with_platform_version(
150 self,
151 platform_version: &PlatformVersion,
152 ) -> Result<Vec<u8>, ProtocolError> {
153 let serialization_format: DataContractInSerializationFormat =
154 self.try_into_platform_versioned(platform_version)?;
155 let config = bincode::config::standard()
156 .with_big_endian()
157 .with_no_limit();
158 bincode::encode_to_vec(serialization_format, config).map_err(|e| {
159 PlatformSerializationError(format!("unable to serialize consume DataContract: {}", e))
160 })
161 }
162}
163
164impl PlatformDeserializableWithPotentialValidationFromVersionedStructure for DataContract {
165 fn versioned_deserialize(
166 data: &[u8],
167 full_validation: bool,
168 platform_version: &PlatformVersion,
169 ) -> Result<Self, ProtocolError>
170 where
171 Self: Sized,
172 {
173 let config = bincode::config::standard()
174 .with_big_endian()
175 .with_no_limit();
176 let data_contract_in_serialization_format: DataContractInSerializationFormat =
177 bincode::borrow_decode_from_slice(data, config)
178 .map_err(|e| {
179 PlatformDeserializationError(format!(
180 "unable to deserialize DataContract: {}",
181 e
182 ))
183 })?
184 .0;
185 DataContract::try_from_platform_versioned(
186 data_contract_in_serialization_format,
187 full_validation,
188 &mut vec![],
189 platform_version,
190 )
191 }
192}
193
194impl PlatformDeserializableWithBytesLenFromVersionedStructure for DataContract {
195 fn versioned_deserialize_with_bytes_len(
196 data: &[u8],
197 full_validation: bool,
198 platform_version: &PlatformVersion,
199 ) -> Result<(Self, usize), ProtocolError>
200 where
201 Self: Sized,
202 {
203 let config = bincode::config::standard()
204 .with_big_endian()
205 .with_no_limit();
206 let (data_contract_in_serialization_format, len) = bincode::borrow_decode_from_slice::<
207 DataContractInSerializationFormat,
208 Configuration<BigEndian>,
209 >(data, config)
210 .map_err(|e| {
211 PlatformDeserializationError(format!("unable to deserialize DataContract: {}", e))
212 })?;
213 Ok((
214 DataContract::try_from_platform_versioned(
215 data_contract_in_serialization_format,
216 full_validation,
217 &mut vec![],
218 platform_version,
219 )?,
220 len,
221 ))
222 }
223}
224
225impl PlatformLimitDeserializableFromVersionedStructure for DataContract {
226 fn versioned_limit_deserialize(
227 data: &[u8],
228 platform_version: &PlatformVersion,
229 ) -> Result<Self, ProtocolError>
230 where
231 Self: Sized,
232 {
233 let config = bincode::config::standard()
234 .with_big_endian()
235 .with_limit::<CONTRACT_DESERIALIZATION_LIMIT>();
236 let data_contract_in_serialization_format: DataContractInSerializationFormat =
237 bincode::borrow_decode_from_slice(data, config)
238 .map_err(|e| {
239 PlatformDeserializationError(format!(
240 "unable to deserialize DataContract with limit: {}",
241 e
242 ))
243 })?
244 .0;
245 DataContract::try_from_platform_versioned(
247 data_contract_in_serialization_format,
248 true,
249 &mut vec![],
250 platform_version,
251 )
252 }
253}
254
255impl DataContract {
256 pub fn as_v0(&self) -> Option<&DataContractV0> {
257 match self {
258 DataContract::V0(v0) => Some(v0),
259 _ => None,
260 }
261 }
262
263 pub fn as_v0_mut(&mut self) -> Option<&mut DataContractV0> {
264 match self {
265 DataContract::V0(v0) => Some(v0),
266 _ => None,
267 }
268 }
269
270 pub fn into_v0(self) -> Option<DataContractV0> {
271 match self {
272 DataContract::V0(v0) => Some(v0),
273 _ => None,
274 }
275 }
276
277 pub fn as_v1(&self) -> Option<&DataContractV1> {
278 match self {
279 DataContract::V1(v1) => Some(v1),
280 _ => None,
281 }
282 }
283
284 pub fn as_v1_mut(&mut self) -> Option<&mut DataContractV1> {
285 match self {
286 DataContract::V1(v1) => Some(v1),
287 _ => None,
288 }
289 }
290
291 pub fn into_v1(self) -> Option<DataContractV1> {
292 match self {
293 DataContract::V1(v1) => Some(v1),
294 _ => None,
295 }
296 }
297
298 #[cfg(test)]
300 pub fn into_latest(self) -> Option<DataContractV1> {
301 self.into_v1()
302 }
303
304 #[cfg(test)]
306 pub fn as_latest(&self) -> Option<&DataContractV1> {
307 match self {
308 DataContract::V1(v1) => Some(v1),
309 _ => None,
310 }
311 }
312
313 #[cfg(test)]
315 pub fn as_latest_mut(&mut self) -> Option<&mut DataContractV1> {
316 match self {
317 DataContract::V1(v1) => Some(v1),
318 _ => None,
319 }
320 }
321
322 pub fn check_version_is_active(
323 protocol_version: u32,
324 data_contract_system_version: FeatureVersion,
325 ) -> Result<bool, ProtocolError> {
326 let platform_version = PlatformVersion::get(protocol_version)?;
327 Ok(platform_version
328 .dpp
329 .contract_versions
330 .contract_structure_version
331 == data_contract_system_version)
332 }
333
334 pub fn hash(&self, platform_version: &PlatformVersion) -> Result<Vec<u8>, ProtocolError> {
335 Ok(hash_double_to_vec(
336 self.serialize_to_bytes_with_platform_version(platform_version)?,
337 ))
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use crate::data_contract::accessors::v0::DataContractV0Getters;
344 use crate::data_contract::config::v0::DataContractConfigGettersV0;
345 use crate::data_contract::document_type::accessors::DocumentTypeV0Getters;
346 use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements;
347 use crate::data_contract::DataContract;
348 use crate::serialization::PlatformDeserializableWithPotentialValidationFromVersionedStructure;
349 use crate::serialization::PlatformSerializableWithPlatformVersion;
350 use crate::system_data_contracts::load_system_data_contract;
351 use crate::tests::fixtures::{
352 get_dashpay_contract_fixture, get_dashpay_contract_with_generalized_encryption_key_fixture,
353 };
354 use crate::version::PlatformVersion;
355 use data_contracts::SystemDataContract::Dashpay;
356
357 #[test]
358 fn test_contract_serialization() {
359 let platform_version = PlatformVersion::latest();
360 let data_contract = load_system_data_contract(Dashpay, platform_version)
361 .expect("expected dashpay contract");
362 let serialized = data_contract
363 .serialize_to_bytes_with_platform_version(platform_version)
364 .expect("expected to serialize data contract");
365 assert_eq!(
366 serialized[0],
367 platform_version
368 .dpp
369 .contract_versions
370 .contract_serialization_version
371 .default_current_version as u8
372 );
373
374 let unserialized = DataContract::versioned_deserialize(&serialized, true, platform_version)
375 .expect("expected to deserialize data contract");
376
377 assert_eq!(data_contract, unserialized);
378 }
379
380 #[test]
381 fn test_contract_can_have_specialized_contract_encryption_decryption_keys() {
382 let data_contract =
383 get_dashpay_contract_with_generalized_encryption_key_fixture(None, 0, 1)
384 .data_contract_owned();
385 assert_eq!(
386 data_contract
387 .config()
388 .requires_identity_decryption_bounded_key(),
389 Some(StorageKeyRequirements::Unique)
390 );
391 assert_eq!(
392 data_contract
393 .config()
394 .requires_identity_encryption_bounded_key(),
395 Some(StorageKeyRequirements::Unique)
396 );
397 }
398
399 #[test]
400 fn test_contract_document_type_can_have_specialized_contract_encryption_decryption_keys() {
401 let data_contract = get_dashpay_contract_fixture(None, 0, 1).data_contract_owned();
402 assert_eq!(
403 data_contract
404 .document_type_for_name("contactRequest")
405 .expect("expected document type")
406 .requires_identity_decryption_bounded_key(),
407 Some(StorageKeyRequirements::MultipleReferenceToLatest)
408 );
409 assert_eq!(
410 data_contract
411 .document_type_for_name("contactRequest")
412 .expect("expected document type")
413 .requires_identity_encryption_bounded_key(),
414 Some(StorageKeyRequirements::MultipleReferenceToLatest)
415 );
416 }
417}