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