dpp/data_contract/factory/v0/
mod.rs1use platform_value::Value;
2use platform_version::TryFromPlatformVersioned;
3
4use crate::consensus::basic::decode::SerializedObjectParsingError;
5use crate::consensus::basic::BasicError;
6use crate::consensus::ConsensusError;
7
8use crate::data_contract::config::DataContractConfig;
9#[cfg(feature = "value-conversion")]
10use crate::data_contract::conversion::value::v0::DataContractValueConversionMethodsV0;
11use crate::data_contract::created_data_contract::CreatedDataContract;
12use crate::data_contract::serialized_version::v0::DataContractInSerializationFormatV0;
13use crate::data_contract::serialized_version::DataContractInSerializationFormat;
14#[cfg(feature = "value-conversion")]
15use crate::data_contract::v0::DataContractV0;
16use crate::data_contract::{DataContract, INITIAL_DATA_CONTRACT_VERSION};
17use crate::serialization::PlatformDeserializableWithPotentialValidationFromVersionedStructureUntrusted;
18#[cfg(feature = "state-transitions")]
19use crate::state_transition::data_contract_create_transition::DataContractCreateTransition;
20#[cfg(feature = "state-transitions")]
21use crate::state_transition::data_contract_update_transition::DataContractUpdateTransition;
22
23#[cfg(feature = "value-conversion")]
24use crate::data_contract::v1::DataContractV1;
25use crate::prelude::IdentityNonce;
26use crate::version::PlatformVersion;
27use crate::{errors::ProtocolError, prelude::Identifier};
28
29pub struct DataContractFactoryV0 {
34 protocol_version: u32,
36}
37
38impl DataContractFactoryV0 {
39 pub fn new(protocol_version: u32) -> Self {
40 Self { protocol_version }
41 }
42
43 pub fn create_with_value_config(
45 &self,
46 owner_id: Identifier,
47 identity_nonce: IdentityNonce,
48 documents: Value,
49 config: Option<Value>,
50 definitions: Option<Value>,
51 ) -> Result<CreatedDataContract, ProtocolError> {
52 let platform_version = PlatformVersion::get(self.protocol_version)?;
53
54 let config = config
56 .map(|config_value| DataContractConfig::from_value(config_value, platform_version))
57 .transpose()?;
58 self.create(owner_id, identity_nonce, documents, config, definitions)
59 }
60
61 pub fn create(
63 &self,
64 owner_id: Identifier,
65 identity_nonce: IdentityNonce,
66 documents: Value,
67 config: Option<DataContractConfig>,
68 definitions: Option<Value>,
69 ) -> Result<CreatedDataContract, ProtocolError> {
70 let data_contract_id =
71 DataContract::generate_data_contract_id_v0(owner_id.to_buffer(), identity_nonce);
72
73 self.create_with_id(
74 data_contract_id,
75 owner_id,
76 identity_nonce,
77 documents,
78 config,
79 definitions,
80 )
81 }
82
83 pub fn create_with_id(
91 &self,
92 data_contract_id: Identifier,
93 owner_id: Identifier,
94 identity_nonce: IdentityNonce,
95 documents: Value,
96 config: Option<DataContractConfig>,
97 definitions: Option<Value>,
98 ) -> Result<CreatedDataContract, ProtocolError> {
99 let platform_version = PlatformVersion::get(self.protocol_version)?;
100
101 let defs = definitions
102 .map(|defs| defs.into_btree_string_map())
103 .transpose()
104 .map_err(ProtocolError::ValueError)?;
105
106 let documents_map = documents
107 .into_btree_string_map()
108 .map_err(ProtocolError::ValueError)?;
109
110 let format = DataContractInSerializationFormat::V0(DataContractInSerializationFormatV0 {
111 id: data_contract_id,
112 config: config.unwrap_or(DataContractConfig::default_for_version(platform_version)?),
113 version: INITIAL_DATA_CONTRACT_VERSION,
114 owner_id,
115 document_schemas: documents_map,
116 schema_defs: defs,
117 });
118
119 let data_contract =
120 DataContract::try_from_platform_versioned(format, true, &mut vec![], platform_version)?;
121
122 CreatedDataContract::from_contract_and_identity_nonce(
123 data_contract,
124 identity_nonce,
125 platform_version,
126 )
127 }
128
129 #[cfg(feature = "value-conversion")]
130 pub fn create_from_object(
132 &self,
133 data_contract_object: Value,
134 full_validation: bool,
135 ) -> Result<DataContract, ProtocolError> {
136 let platform_version = PlatformVersion::get(self.protocol_version)?;
137 match platform_version
138 .dpp
139 .contract_versions
140 .contract_structure_version
141 {
142 0 => {
143 let v0 = if full_validation {
144 DataContractV0::from_value(data_contract_object, true, platform_version)?
145 } else {
146 platform_value::from_value::<DataContractV0>(data_contract_object)
147 .map_err(ProtocolError::ValueError)?
148 };
149 Ok(v0.into())
150 }
151 1 => {
152 let v1 = if full_validation {
153 DataContractV1::from_value(data_contract_object, true, platform_version)?
154 } else {
155 platform_value::from_value::<DataContractV1>(data_contract_object)
156 .map_err(ProtocolError::ValueError)?
157 };
158 Ok(v1.into())
159 }
160 version => Err(ProtocolError::UnknownVersionMismatch {
161 method: "DataContractFactoryV0::create_from_object".to_string(),
162 known_versions: vec![0, 1],
163 received: version,
164 }),
165 }
166 }
167
168 pub fn create_from_buffer(
170 &self,
171 buffer: Vec<u8>,
172 #[cfg(feature = "validation")] skip_validation: bool,
173 ) -> Result<DataContract, ProtocolError> {
174 let platform_version = PlatformVersion::get(self.protocol_version)?;
175 #[cfg(not(feature = "validation"))]
176 let skip_validation = true;
177
178 let data_contract: DataContract = DataContract::versioned_deserialize_untrusted(
179 buffer.as_slice(),
180 !skip_validation,
181 platform_version,
182 )
183 .map_err(|e| {
184 ConsensusError::BasicError(BasicError::SerializedObjectParsingError(
185 SerializedObjectParsingError::new(format!("Decode protocol entity: {:#?}", e)),
186 ))
187 })?;
188
189 Ok(data_contract)
199 }
200
201 #[cfg(feature = "state-transitions")]
219 pub fn create_unsigned_data_contract_create_transition(
220 &self,
221 created_data_contract: CreatedDataContract,
222 ) -> Result<DataContractCreateTransition, ProtocolError> {
223 DataContractCreateTransition::try_from_platform_versioned(
224 created_data_contract,
225 PlatformVersion::get(self.protocol_version)?,
226 )
227 }
228
229 #[cfg(feature = "state-transitions")]
230 pub fn create_unsigned_data_contract_update_transition(
231 &self,
232 data_contract: DataContract,
233 identity_contract_nonce: IdentityNonce,
234 ) -> Result<DataContractUpdateTransition, ProtocolError> {
235 DataContractUpdateTransition::try_from_platform_versioned(
236 (data_contract, identity_contract_nonce),
237 PlatformVersion::get(self.protocol_version)?,
238 )
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use crate::data_contract::accessors::v0::DataContractV0Getters;
246 use crate::data_contract::schema::DataContractSchemaMethodsV0;
247
248 use crate::serialization::PlatformSerializableWithPlatformVersion;
249 use crate::state_transition::data_contract_create_transition::accessors::DataContractCreateTransitionAccessorsV0;
250 use crate::state_transition::StateTransitionLike;
251 use crate::tests::fixtures::get_data_contract_fixture;
252
253 pub struct TestData {
254 created_data_contract: CreatedDataContract,
255 raw_data_contract: Value,
256 factory: DataContractFactoryV0,
257 }
258
259 fn get_test_data() -> TestData {
260 let platform_version = PlatformVersion::latest();
261 let created_data_contract =
262 get_data_contract_fixture(None, 0, platform_version.protocol_version);
263
264 let raw_data_contract =
265 platform_value::to_value(created_data_contract.data_contract()).unwrap();
266
267 let factory = DataContractFactoryV0::new(platform_version.protocol_version);
268 TestData {
269 created_data_contract,
270 raw_data_contract,
271 factory,
272 }
273 }
274
275 #[test]
276 fn should_create_data_contract_with_specified_name_and_docs_definition() {
277 let TestData {
278 created_data_contract,
279 raw_data_contract,
280 factory,
281 } = get_test_data();
282
283 let data_contract = created_data_contract.data_contract_owned();
284
285 let raw_defs = raw_data_contract
286 .get_value("schemaDefs")
287 .expect("documents property should exist")
288 .clone();
289
290 let raw_documents = raw_data_contract
291 .get_value("documentSchemas")
292 .expect("documents property should exist")
293 .clone();
294
295 let result = factory
296 .create_with_value_config(
297 data_contract.owner_id(),
298 1,
299 raw_documents,
300 None,
301 Some(raw_defs),
302 )
303 .expect("Data Contract should be created")
304 .data_contract_owned();
305
306 assert_eq!(data_contract.version(), result.version());
307 assert_eq!(data_contract.id().len(), result.id().len());
309 assert_ne!(data_contract.id(), result.id());
310 assert_eq!(data_contract.schema_defs(), result.schema_defs());
311 assert_eq!(data_contract.document_schemas(), result.document_schemas());
312 assert_eq!(data_contract.owner_id(), result.owner_id());
313 }
314
315 #[tokio::test]
316 async fn should_crate_data_contract_from_object() {
317 let _platform_version = PlatformVersion::latest();
318
319 let TestData {
320 created_data_contract,
321 raw_data_contract,
322 factory,
323 } = get_test_data();
324
325 let data_contract = created_data_contract.data_contract_owned();
326
327 let result = factory
328 .create_from_object(raw_data_contract, false)
329 .expect("Data Contract should be created");
330
331 assert_eq!(data_contract.version(), result.version());
332 assert_eq!(data_contract.id(), result.id());
333 assert_eq!(data_contract.owner_id(), result.owner_id());
334 assert_eq!(data_contract.document_types(), result.document_types());
335 }
336
337 #[tokio::test]
338 async fn should_create_data_contract_from_buffer() {
339 let platform_version = PlatformVersion::latest();
340
341 let TestData {
342 created_data_contract,
343 factory,
344 ..
345 } = get_test_data();
346
347 let data_contract = created_data_contract.data_contract_owned();
348
349 let serialized_data_contract = data_contract
350 .serialize_to_bytes_with_platform_version(platform_version)
351 .expect("should be serialized to buffer");
352 let result = factory
353 .create_from_buffer(serialized_data_contract, false)
354 .expect("Data Contract should be created from the buffer");
355
356 assert_eq!(data_contract.version(), result.version());
357 assert_eq!(data_contract.id(), result.id());
358 assert_eq!(data_contract.owner_id(), result.owner_id());
359 assert_eq!(data_contract.document_types(), result.document_types());
360 }
361
362 #[test]
363 fn should_create_data_contract_create_transition_from_data_contract() {
364 let platform_version = PlatformVersion::latest();
365
366 let TestData {
367 created_data_contract,
368 factory,
369 raw_data_contract,
370 } = get_test_data();
371
372 let result = factory
373 .create_unsigned_data_contract_create_transition(created_data_contract.clone())
374 .expect("Data Contract Transition should be created");
375
376 assert_eq!(
377 platform_version
378 .dpp
379 .state_transition_serialization_versions
380 .contract_create_state_transition
381 .default_current_version,
382 result.state_transition_protocol_version()
383 );
384 assert_eq!(
385 created_data_contract.identity_nonce(),
386 result.identity_nonce()
387 );
388
389 let contract_value = platform_value::to_value(
390 DataContract::try_from_platform_versioned(
391 result.data_contract().to_owned(),
392 false,
393 &mut vec![],
394 platform_version,
395 )
396 .unwrap(),
397 )
398 .unwrap();
399
400 assert_eq!(raw_data_contract, contract_value);
401 }
402}