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::PlatformDeserializableWithPotentialValidationFromVersionedStructure;
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 platform_version = PlatformVersion::get(self.protocol_version)?;
71
72 let data_contract_id =
73 DataContract::generate_data_contract_id_v0(owner_id.to_buffer(), identity_nonce);
74
75 let defs = definitions
76 .map(|defs| defs.into_btree_string_map())
77 .transpose()
78 .map_err(ProtocolError::ValueError)?;
79
80 let documents_map = documents
81 .into_btree_string_map()
82 .map_err(ProtocolError::ValueError)?;
83
84 let format = DataContractInSerializationFormat::V0(DataContractInSerializationFormatV0 {
85 id: data_contract_id,
86 config: config.unwrap_or(DataContractConfig::default_for_version(platform_version)?),
87 version: INITIAL_DATA_CONTRACT_VERSION,
88 owner_id,
89 document_schemas: documents_map,
90 schema_defs: defs,
91 });
92
93 let data_contract =
94 DataContract::try_from_platform_versioned(format, true, &mut vec![], platform_version)?;
95
96 CreatedDataContract::from_contract_and_identity_nonce(
97 data_contract,
98 identity_nonce,
99 platform_version,
100 )
101 }
102
103 #[cfg(feature = "value-conversion")]
104 pub fn create_from_object(
106 &self,
107 data_contract_object: Value,
108 full_validation: bool,
109 ) -> Result<DataContract, ProtocolError> {
110 let platform_version = PlatformVersion::get(self.protocol_version)?;
111 match platform_version
112 .dpp
113 .contract_versions
114 .contract_structure_version
115 {
116 0 => {
117 let v0 = if full_validation {
118 DataContractV0::from_value(data_contract_object, true, platform_version)?
119 } else {
120 platform_value::from_value::<DataContractV0>(data_contract_object)
121 .map_err(ProtocolError::ValueError)?
122 };
123 Ok(v0.into())
124 }
125 1 => {
126 let v1 = if full_validation {
127 DataContractV1::from_value(data_contract_object, true, platform_version)?
128 } else {
129 platform_value::from_value::<DataContractV1>(data_contract_object)
130 .map_err(ProtocolError::ValueError)?
131 };
132 Ok(v1.into())
133 }
134 version => Err(ProtocolError::UnknownVersionMismatch {
135 method: "DataContractFactoryV0::create_from_object".to_string(),
136 known_versions: vec![0, 1],
137 received: version,
138 }),
139 }
140 }
141
142 pub fn create_from_buffer(
144 &self,
145 buffer: Vec<u8>,
146 #[cfg(feature = "validation")] skip_validation: bool,
147 ) -> Result<DataContract, ProtocolError> {
148 let platform_version = PlatformVersion::get(self.protocol_version)?;
149 #[cfg(not(feature = "validation"))]
150 let skip_validation = true;
151
152 let data_contract: DataContract = DataContract::versioned_deserialize(
153 buffer.as_slice(),
154 !skip_validation,
155 platform_version,
156 )
157 .map_err(|e| {
158 ConsensusError::BasicError(BasicError::SerializedObjectParsingError(
159 SerializedObjectParsingError::new(format!("Decode protocol entity: {:#?}", e)),
160 ))
161 })?;
162
163 Ok(data_contract)
173 }
174
175 #[cfg(feature = "state-transitions")]
193 pub fn create_unsigned_data_contract_create_transition(
194 &self,
195 created_data_contract: CreatedDataContract,
196 ) -> Result<DataContractCreateTransition, ProtocolError> {
197 DataContractCreateTransition::try_from_platform_versioned(
198 created_data_contract,
199 PlatformVersion::get(self.protocol_version)?,
200 )
201 }
202
203 #[cfg(feature = "state-transitions")]
204 pub fn create_unsigned_data_contract_update_transition(
205 &self,
206 data_contract: DataContract,
207 identity_contract_nonce: IdentityNonce,
208 ) -> Result<DataContractUpdateTransition, ProtocolError> {
209 DataContractUpdateTransition::try_from_platform_versioned(
210 (data_contract, identity_contract_nonce),
211 PlatformVersion::get(self.protocol_version)?,
212 )
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use crate::data_contract::accessors::v0::DataContractV0Getters;
220 use crate::data_contract::schema::DataContractSchemaMethodsV0;
221
222 use crate::serialization::PlatformSerializableWithPlatformVersion;
223 use crate::state_transition::data_contract_create_transition::accessors::DataContractCreateTransitionAccessorsV0;
224 use crate::state_transition::StateTransitionLike;
225 use crate::tests::fixtures::get_data_contract_fixture;
226
227 pub struct TestData {
228 created_data_contract: CreatedDataContract,
229 raw_data_contract: Value,
230 factory: DataContractFactoryV0,
231 }
232
233 fn get_test_data() -> TestData {
234 let platform_version = PlatformVersion::latest();
235 let created_data_contract =
236 get_data_contract_fixture(None, 0, platform_version.protocol_version);
237
238 let raw_data_contract =
239 platform_value::to_value(created_data_contract.data_contract()).unwrap();
240
241 let factory = DataContractFactoryV0::new(platform_version.protocol_version);
242 TestData {
243 created_data_contract,
244 raw_data_contract,
245 factory,
246 }
247 }
248
249 #[test]
250 fn should_create_data_contract_with_specified_name_and_docs_definition() {
251 let TestData {
252 created_data_contract,
253 raw_data_contract,
254 factory,
255 } = get_test_data();
256
257 let data_contract = created_data_contract.data_contract_owned();
258
259 let raw_defs = raw_data_contract
260 .get_value("schemaDefs")
261 .expect("documents property should exist")
262 .clone();
263
264 let raw_documents = raw_data_contract
265 .get_value("documentSchemas")
266 .expect("documents property should exist")
267 .clone();
268
269 let result = factory
270 .create_with_value_config(
271 data_contract.owner_id(),
272 1,
273 raw_documents,
274 None,
275 Some(raw_defs),
276 )
277 .expect("Data Contract should be created")
278 .data_contract_owned();
279
280 assert_eq!(data_contract.version(), result.version());
281 assert_eq!(data_contract.id().len(), result.id().len());
283 assert_ne!(data_contract.id(), result.id());
284 assert_eq!(data_contract.schema_defs(), result.schema_defs());
285 assert_eq!(data_contract.document_schemas(), result.document_schemas());
286 assert_eq!(data_contract.owner_id(), result.owner_id());
287 }
288
289 #[tokio::test]
290 async fn should_crate_data_contract_from_object() {
291 let _platform_version = PlatformVersion::latest();
292
293 let TestData {
294 created_data_contract,
295 raw_data_contract,
296 factory,
297 } = get_test_data();
298
299 let data_contract = created_data_contract.data_contract_owned();
300
301 let result = factory
302 .create_from_object(raw_data_contract, false)
303 .expect("Data Contract should be created");
304
305 assert_eq!(data_contract.version(), result.version());
306 assert_eq!(data_contract.id(), result.id());
307 assert_eq!(data_contract.owner_id(), result.owner_id());
308 assert_eq!(data_contract.document_types(), result.document_types());
309 }
310
311 #[tokio::test]
312 async fn should_create_data_contract_from_buffer() {
313 let platform_version = PlatformVersion::latest();
314
315 let TestData {
316 created_data_contract,
317 factory,
318 ..
319 } = get_test_data();
320
321 let data_contract = created_data_contract.data_contract_owned();
322
323 let serialized_data_contract = data_contract
324 .serialize_to_bytes_with_platform_version(platform_version)
325 .expect("should be serialized to buffer");
326 let result = factory
327 .create_from_buffer(serialized_data_contract, false)
328 .expect("Data Contract should be created from the buffer");
329
330 assert_eq!(data_contract.version(), result.version());
331 assert_eq!(data_contract.id(), result.id());
332 assert_eq!(data_contract.owner_id(), result.owner_id());
333 assert_eq!(data_contract.document_types(), result.document_types());
334 }
335
336 #[test]
337 fn should_create_data_contract_create_transition_from_data_contract() {
338 let platform_version = PlatformVersion::latest();
339
340 let TestData {
341 created_data_contract,
342 factory,
343 raw_data_contract,
344 } = get_test_data();
345
346 let result = factory
347 .create_unsigned_data_contract_create_transition(created_data_contract.clone())
348 .expect("Data Contract Transition should be created");
349
350 assert_eq!(0, result.state_transition_protocol_version());
351 assert_eq!(
352 created_data_contract.identity_nonce(),
353 result.identity_nonce()
354 );
355
356 let contract_value = platform_value::to_value(
357 &DataContract::try_from_platform_versioned(
358 result.data_contract().to_owned(),
359 false,
360 &mut vec![],
361 platform_version,
362 )
363 .unwrap(),
364 )
365 .unwrap();
366
367 assert_eq!(raw_data_contract, contract_value);
368 }
369}