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 => Ok(DataContractV0::from_value(
117 data_contract_object,
118 full_validation,
119 platform_version,
120 )?
121 .into()),
122 1 => Ok(DataContractV1::from_value(
123 data_contract_object,
124 full_validation,
125 platform_version,
126 )?
127 .into()),
128 version => Err(ProtocolError::UnknownVersionMismatch {
129 method: "DataContractFactoryV0::create_from_object".to_string(),
130 known_versions: vec![0, 1],
131 received: version,
132 }),
133 }
134 }
135
136 pub fn create_from_buffer(
138 &self,
139 buffer: Vec<u8>,
140 #[cfg(feature = "validation")] skip_validation: bool,
141 ) -> Result<DataContract, ProtocolError> {
142 let platform_version = PlatformVersion::get(self.protocol_version)?;
143 #[cfg(not(feature = "validation"))]
144 let skip_validation = true;
145
146 let data_contract: DataContract = DataContract::versioned_deserialize(
147 buffer.as_slice(),
148 !skip_validation,
149 platform_version,
150 )
151 .map_err(|e| {
152 ConsensusError::BasicError(BasicError::SerializedObjectParsingError(
153 SerializedObjectParsingError::new(format!("Decode protocol entity: {:#?}", e)),
154 ))
155 })?;
156
157 Ok(data_contract)
167 }
168
169 #[cfg(feature = "state-transitions")]
187 pub fn create_unsigned_data_contract_create_transition(
188 &self,
189 created_data_contract: CreatedDataContract,
190 ) -> Result<DataContractCreateTransition, ProtocolError> {
191 DataContractCreateTransition::try_from_platform_versioned(
192 created_data_contract,
193 PlatformVersion::get(self.protocol_version)?,
194 )
195 }
196
197 #[cfg(feature = "state-transitions")]
198 pub fn create_unsigned_data_contract_update_transition(
199 &self,
200 data_contract: DataContract,
201 identity_contract_nonce: IdentityNonce,
202 ) -> Result<DataContractUpdateTransition, ProtocolError> {
203 DataContractUpdateTransition::try_from_platform_versioned(
204 (data_contract, identity_contract_nonce),
205 PlatformVersion::get(self.protocol_version)?,
206 )
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use crate::data_contract::accessors::v0::DataContractV0Getters;
214 use crate::data_contract::schema::DataContractSchemaMethodsV0;
215
216 use crate::serialization::PlatformSerializableWithPlatformVersion;
217 use crate::state_transition::data_contract_create_transition::accessors::DataContractCreateTransitionAccessorsV0;
218 use crate::state_transition::StateTransitionLike;
219 use crate::tests::fixtures::get_data_contract_fixture;
220
221 pub struct TestData {
222 created_data_contract: CreatedDataContract,
223 raw_data_contract: Value,
224 factory: DataContractFactoryV0,
225 }
226
227 fn get_test_data() -> TestData {
228 let platform_version = PlatformVersion::latest();
229 let created_data_contract =
230 get_data_contract_fixture(None, 0, platform_version.protocol_version);
231
232 let raw_data_contract = created_data_contract
233 .data_contract()
234 .to_value(platform_version)
235 .unwrap();
236
237 let factory = DataContractFactoryV0::new(platform_version.protocol_version);
238 TestData {
239 created_data_contract,
240 raw_data_contract,
241 factory,
242 }
243 }
244
245 #[test]
246 fn should_create_data_contract_with_specified_name_and_docs_definition() {
247 let TestData {
248 created_data_contract,
249 raw_data_contract,
250 factory,
251 } = get_test_data();
252
253 let data_contract = created_data_contract.data_contract_owned();
254
255 let raw_defs = raw_data_contract
256 .get_value("schemaDefs")
257 .expect("documents property should exist")
258 .clone();
259
260 let raw_documents = raw_data_contract
261 .get_value("documentSchemas")
262 .expect("documents property should exist")
263 .clone();
264
265 let result = factory
266 .create_with_value_config(
267 data_contract.owner_id(),
268 1,
269 raw_documents,
270 None,
271 Some(raw_defs),
272 )
273 .expect("Data Contract should be created")
274 .data_contract_owned();
275
276 assert_eq!(data_contract.version(), result.version());
277 assert_eq!(data_contract.id().len(), result.id().len());
279 assert_ne!(data_contract.id(), result.id());
280 assert_eq!(data_contract.schema_defs(), result.schema_defs());
281 assert_eq!(data_contract.document_schemas(), result.document_schemas());
282 assert_eq!(data_contract.owner_id(), result.owner_id());
283 }
284
285 #[tokio::test]
286 async fn should_crate_data_contract_from_object() {
287 let _platform_version = PlatformVersion::latest();
288
289 let TestData {
290 created_data_contract,
291 raw_data_contract,
292 factory,
293 } = get_test_data();
294
295 let data_contract = created_data_contract.data_contract_owned();
296
297 let result = factory
298 .create_from_object(raw_data_contract, false)
299 .expect("Data Contract should be created");
300
301 assert_eq!(data_contract.version(), result.version());
302 assert_eq!(data_contract.id(), result.id());
303 assert_eq!(data_contract.owner_id(), result.owner_id());
304 assert_eq!(data_contract.document_types(), result.document_types());
305 }
306
307 #[tokio::test]
308 async fn should_create_data_contract_from_buffer() {
309 let platform_version = PlatformVersion::latest();
310
311 let TestData {
312 created_data_contract,
313 factory,
314 ..
315 } = get_test_data();
316
317 let data_contract = created_data_contract.data_contract_owned();
318
319 let serialized_data_contract = data_contract
320 .serialize_to_bytes_with_platform_version(platform_version)
321 .expect("should be serialized to buffer");
322 let result = factory
323 .create_from_buffer(serialized_data_contract, false)
324 .expect("Data Contract should be created from the buffer");
325
326 assert_eq!(data_contract.version(), result.version());
327 assert_eq!(data_contract.id(), result.id());
328 assert_eq!(data_contract.owner_id(), result.owner_id());
329 assert_eq!(data_contract.document_types(), result.document_types());
330 }
331
332 #[test]
333 fn should_create_data_contract_create_transition_from_data_contract() {
334 let platform_version = PlatformVersion::latest();
335
336 let TestData {
337 created_data_contract,
338 factory,
339 raw_data_contract,
340 } = get_test_data();
341
342 let result = factory
343 .create_unsigned_data_contract_create_transition(created_data_contract.clone())
344 .expect("Data Contract Transition should be created");
345
346 assert_eq!(0, result.state_transition_protocol_version());
347 assert_eq!(
348 created_data_contract.identity_nonce(),
349 result.identity_nonce()
350 );
351
352 let contract_value = DataContract::try_from_platform_versioned(
353 result.data_contract().to_owned(),
354 false,
355 &mut vec![],
356 platform_version,
357 )
358 .unwrap()
359 .to_value(platform_version)
360 .unwrap();
361
362 assert_eq!(raw_data_contract, contract_value);
363 }
364}