Skip to main content

dpp/tests/
json_document.rs

1use crate::data_contract::accessors::v0::DataContractV0Setters;
2#[cfg(feature = "json-conversion")]
3use crate::data_contract::config::DataContractConfig;
4#[cfg(feature = "json-conversion")]
5use crate::data_contract::conversion::json::DataContractJsonConversionMethodsV0;
6#[cfg(any(feature = "state-transitions", feature = "factories"))]
7use crate::data_contract::created_data_contract::v0::CreatedDataContractV0;
8#[cfg(any(feature = "state-transitions", feature = "factories"))]
9use crate::data_contract::created_data_contract::CreatedDataContract;
10use crate::data_contract::document_type::accessors::DocumentTypeV0Getters;
11use crate::data_contract::document_type::DocumentTypeRef;
12use crate::document::{Document, DocumentV0};
13use crate::prelude::{DataContract, IdentityNonce};
14#[cfg(feature = "data-contract-cbor-conversion")]
15use crate::util::cbor_serializer::serializable_value_to_cbor;
16use crate::version::PlatformVersion;
17use crate::ProtocolError;
18use platform_value::{Identifier, ReplacementType};
19use std::fs::File;
20use std::io::BufReader;
21use std::path::Path;
22
23/// Reads a JSON file and converts it to serde_value.
24#[cfg(feature = "json-conversion")]
25pub fn json_document_to_json_value(
26    path: impl AsRef<Path>,
27) -> Result<serde_json::Value, ProtocolError> {
28    let file = File::open(path.as_ref()).map_err(|_| {
29        ProtocolError::FileNotFound(format!(
30            "file not found at path {}",
31            path.as_ref().to_str().unwrap()
32        ))
33    })?;
34
35    let reader = BufReader::new(file);
36    serde_json::from_reader(reader)
37        .map_err(|_| ProtocolError::DecodingError("error decoding value from document".to_string()))
38}
39
40/// Reads a JSON file and converts it to serde_value.
41pub fn json_document_to_platform_value(
42    path: impl AsRef<Path>,
43) -> Result<platform_value::Value, ProtocolError> {
44    let file = File::open(path.as_ref()).map_err(|_| {
45        ProtocolError::FileNotFound(format!(
46            "file not found at path {}",
47            path.as_ref().to_str().unwrap()
48        ))
49    })?;
50
51    let reader = BufReader::new(file);
52    serde_json::from_reader(reader)
53        .map_err(|_| ProtocolError::DecodingError("error decoding value from document".to_string()))
54}
55
56/// Reads a JSON file and converts it to CBOR.
57#[cfg(feature = "data-contract-cbor-conversion")]
58pub fn json_document_to_cbor(
59    path: impl AsRef<Path>,
60    protocol_version: Option<u32>,
61) -> Result<Vec<u8>, ProtocolError> {
62    let json = json_document_to_json_value(path)?;
63    serializable_value_to_cbor(&json, protocol_version)
64}
65
66/// Reads a JSON file and converts it a contract.
67#[cfg(feature = "json-conversion")]
68pub fn json_document_to_contract(
69    path: impl AsRef<Path>,
70    full_validation: bool,
71    platform_version: &PlatformVersion,
72) -> Result<DataContract, ProtocolError> {
73    let value = json_document_to_json_value(path)?;
74
75    if full_validation {
76        DataContract::from_json(value, true, platform_version)
77    } else {
78        // Non-validating path: deserialize the platform-version-agnostic
79        // serialization format (which handles both V0/V1 wire shapes via
80        // `$formatVersion`), then dispatch on the caller-provided
81        // `platform_version` to pick the DataContract variant. We avoid
82        // `serde_json::from_value::<DataContract>` here because that path
83        // ignores the caller pv and uses the process-global current/latest.
84        let format: crate::data_contract::serialized_version::DataContractInSerializationFormat =
85            serde_json::from_value(value)
86                .map_err(|e| ProtocolError::DecodingError(e.to_string()))?;
87        DataContract::try_from_platform_versioned(format, false, &mut vec![], platform_version)
88    }
89}
90
91#[cfg(all(
92    any(feature = "state-transitions", feature = "factories"),
93    feature = "json-conversion"
94))]
95/// Reads a JSON file and converts it a contract.
96pub fn json_document_to_created_contract(
97    path: impl AsRef<Path>,
98    identity_nonce: IdentityNonce,
99    full_validation: bool,
100    platform_version: &PlatformVersion,
101) -> Result<CreatedDataContract, ProtocolError> {
102    let mut data_contract = json_document_to_contract(path, full_validation, platform_version)?;
103
104    // JSON fixtures typically lack a config field, so they deserialize with V0 config default.
105    // Set config to the platform version default to match what the factory would produce,
106    // ensuring the contract passes config min_version validation during state transition processing.
107    data_contract.set_config(DataContractConfig::default_for_version(platform_version)?);
108
109    Ok(CreatedDataContractV0 {
110        data_contract,
111        identity_nonce,
112    }
113    .into())
114}
115
116/// Reads a JSON file and converts it a document.
117#[cfg(feature = "json-conversion")]
118pub fn json_document_to_contract_with_ids(
119    path: impl AsRef<Path>,
120    id: Option<Identifier>,
121    owner_id: Option<Identifier>,
122    full_validation: bool,
123    platform_version: &PlatformVersion,
124) -> Result<DataContract, ProtocolError> {
125    let value = json_document_to_json_value(path)?;
126
127    let mut contract = DataContract::from_json(value, full_validation, platform_version)?;
128
129    if let Some(id) = id {
130        contract.set_id(id);
131    }
132
133    if let Some(owner_id) = owner_id {
134        contract.set_owner_id(owner_id);
135    }
136
137    Ok(contract)
138}
139
140/// Reads a JSON file and converts it a document.
141pub fn json_document_to_document(
142    path: impl AsRef<Path>,
143    owner_id: Option<Identifier>,
144    document_type: DocumentTypeRef,
145    _platform_version: &PlatformVersion,
146) -> Result<Document, ProtocolError> {
147    let mut data = json_document_to_platform_value(path)?;
148
149    if let Some(owner_id) = owner_id {
150        data.set_value(
151            "$ownerId",
152            platform_value::Value::Identifier(owner_id.into_buffer()),
153        )?;
154    }
155
156    let mut document: DocumentV0 = DocumentV0 {
157        id: data.remove_identifier("$id")?,
158        owner_id: data.remove_identifier("$ownerId")?,
159        properties: Default::default(),
160        revision: data.remove_optional_integer("$revision")?,
161        created_at: data.remove_optional_integer("$createdAt")?,
162        updated_at: data.remove_optional_integer("$updatedAt")?,
163        transferred_at: data.remove_optional_integer("$transferredAt")?,
164        created_at_block_height: data.remove_optional_integer("$createdAtBlockHeight")?,
165        updated_at_block_height: data.remove_optional_integer("$updatedAtBlockHeight")?,
166        transferred_at_block_height: data.remove_optional_integer("$transferredAtBlockHeight")?,
167        created_at_core_block_height: data.remove_optional_integer("$createdAtCoreBlockHeight")?,
168        updated_at_core_block_height: data.remove_optional_integer("$updatedAtCoreBlockHeight")?,
169        transferred_at_core_block_height: data
170            .remove_optional_integer("$transferredAtCoreBlockHeight")?,
171        creator_id: data.remove_optional_identifier("$creatorId")?,
172    };
173
174    data.replace_at_paths(
175        document_type.identifier_paths().iter().map(|s| s.as_str()),
176        ReplacementType::Identifier,
177    )?;
178
179    data.replace_at_paths(
180        document_type.binary_paths().iter().map(|s| s.as_str()),
181        ReplacementType::BinaryBytes,
182    )?;
183
184    document.properties = data
185        .into_btree_string_map()
186        .map_err(ProtocolError::ValueError)?;
187
188    Ok(document.into())
189}