diff --git a/packages/wasm-dpp/src/data_contract_factory/data_contract_factory.rs b/packages/wasm-dpp/src/data_contract_factory/data_contract_factory.rs index baa24a4795f..7d11db2ba02 100644 --- a/packages/wasm-dpp/src/data_contract_factory/data_contract_factory.rs +++ b/packages/wasm-dpp/src/data_contract_factory/data_contract_factory.rs @@ -51,8 +51,8 @@ impl DataContractValidatorWasm { let parameters: DataContractParameters = with_js_error!(serde_wasm_bindgen::from_value(raw_data_contract))?; let json_object = serde_json::to_value(parameters).expect("Implements Serialize"); - let validation_result = self.0.validate(&json_object).map_err(from_protocol_error)?; + let validation_result = self.0.validate(&json_object).map_err(from_protocol_error)?; Ok(validation_result.map(|_| JsValue::undefined()).into()) } } diff --git a/packages/wasm-dpp/src/document/fetch_and_validate_data_contract.rs b/packages/wasm-dpp/src/document/fetch_and_validate_data_contract.rs new file mode 100644 index 00000000000..7e1be765675 --- /dev/null +++ b/packages/wasm-dpp/src/document/fetch_and_validate_data_contract.rs @@ -0,0 +1,40 @@ +use dpp::{ + document::{self, fetch_and_validate_data_contract::fetch_and_validate_data_contract}, + prelude::DataContract, + state_transition::state_transition_execution_context::StateTransitionExecutionContext, + util::json_value::{JsonValueExt, ReplaceWith}, + validation::ValidationResult, +}; +use wasm_bindgen::prelude::*; + +use crate::{ + state_repository::{ExternalStateRepositoryLike, ExternalStateRepositoryLikeWrapper}, + utils::{ToSerdeJSONExt, WithJsError}, + validation::ValidationResultWasm, + DataContractWasm, +}; + +#[wasm_bindgen(js_name = fetchAndValidateDataContract)] +pub async fn fetch_and_validate_data_contract_wasm( + state_repository: ExternalStateRepositoryLike, + js_raw_document: JsValue, +) -> Result { + let wrapped_state_repository = ExternalStateRepositoryLikeWrapper::new(state_repository); + let mut document_value = js_raw_document.with_serde_to_json_value()?; + + // Allow to fail as identifier can be type of Buffer or Identifier + // We don't need to replace dynamic values because the function doesn't use them + let _ = + document_value.replace_identifier_paths(document::IDENTIFIER_FIELDS, ReplaceWith::Bytes); + + // TODO! remove the context. The the providing the context in state repository should be optional + let ctx = StateTransitionExecutionContext::default(); + let validation_result = + fetch_and_validate_data_contract(&wrapped_state_repository, &document_value, &ctx) + .await + .with_js_error()?; + let result_with_js_value: ValidationResult = validation_result + .map(|dc| >::from(dc).into()); + + Ok(result_with_js_value.into()) +} diff --git a/packages/wasm-dpp/src/document/mod.rs b/packages/wasm-dpp/src/document/mod.rs index 2b2c25d0c56..c32e857de39 100644 --- a/packages/wasm-dpp/src/document/mod.rs +++ b/packages/wasm-dpp/src/document/mod.rs @@ -4,7 +4,7 @@ use dpp::util::json_schema::JsonSchemaExt; use dpp::util::json_value::{JsonValueExt, ReplaceWith}; use dpp::util::string_encoding::Encoding; use serde::{Deserialize, Serialize}; -use std::convert::TryInto; +use std::convert::{self, TryInto}; use wasm_bindgen::prelude::*; use dpp::document::{property_names, Document, IDENTIFIER_FIELDS}; @@ -21,6 +21,7 @@ use crate::{DataContractWasm, MetadataWasm}; pub mod errors; pub use state_transition::*; mod factory; +pub mod fetch_and_validate_data_contract; pub mod state_transition; mod validator; @@ -28,18 +29,19 @@ pub use document_batch_transition::{DocumentsBatchTransitionWASM, DocumentsConta pub use factory::DocumentFactoryWASM; pub use validator::DocumentValidatorWasm; +#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ConversionOptions { + #[serde(default)] + pub skip_identifiers_conversion: bool, +} + pub(super) enum BinaryType { Identifier, Buffer, None, } -#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)] -#[serde(rename_all = "camelCase")] -pub struct ConversionOptions { - skip_identifiers_conversion: bool, -} - #[wasm_bindgen(js_name=Document)] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DocumentWasm(Document); @@ -155,9 +157,35 @@ impl DocumentWasm { #[wasm_bindgen(js_name=getData)] pub fn get_data(&mut self) -> Result { - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + let js_value = self + .0 + .data + .serialize(&serde_wasm_bindgen::Serializer::json_compatible())?; - Ok(with_js_error!(self.0.data.serialize(&serializer))?) + let (identifier_paths, binary_paths) = self + .0 + .data_contract + .get_identifiers_and_binary_paths(&self.0.document_type) + .with_js_error()?; + + for path in identifier_paths { + if let Ok(value) = self.0.data.get_value(path) { + let bytes: Vec = serde_json::from_value(value.to_owned()).with_js_error()?; + let id = >::from( + Identifier::from_bytes(&bytes).unwrap(), + ); + lodash_set(&js_value, path, id.into()); + } + } + for path in binary_paths { + if let Ok(value) = self.0.data.get_value(path) { + let bytes: Vec = serde_json::from_value(value.to_owned()).with_js_error()?; + let buffer = Buffer::from_bytes(&bytes); + lodash_set(&js_value, path, buffer.into()); + } + } + + Ok(js_value) } #[wasm_bindgen(js_name=set)] diff --git a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_create_transition.rs b/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_create_transition.rs index 067f30696c3..a5bc75ae362 100644 --- a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_create_transition.rs +++ b/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_create_transition.rs @@ -1,14 +1,26 @@ +use std::convert; + use dpp::{ document::document_transition::{ - document_create_transition, DocumentCreateTransition, DocumentTransitionObjectLike, + self, document_create_transition, DocumentCreateTransition, DocumentTransitionObjectLike, + }, + prelude::{DataContract, Identifier}, + util::{ + json_schema::JsonSchemaExt, + json_value::{JsonValueExt, ReplaceWith}, }, - util::json_value::JsonValueExt, }; use serde::Serialize; use wasm_bindgen::prelude::*; use crate::{ - buffer::Buffer, identifier::IdentifierWrapper, lodash::lodash_set, utils::WithJsError, + buffer::Buffer, + document, + document_batch_transition::document_transition::to_object, + identifier::IdentifierWrapper, + lodash::lodash_set, + utils::{ToSerdeJSONExt, WithJsError}, + BinaryType, DataContractWasm, }; #[wasm_bindgen(js_name=DocumentCreateTransition)] @@ -25,39 +37,211 @@ impl From for DocumentCreateTransitionWasm { #[wasm_bindgen(js_class=DocumentCreateTransition)] impl DocumentCreateTransitionWasm { + #[wasm_bindgen(constructor)] + pub fn from_raw_object( + raw_object: JsValue, + data_contract: &DataContractWasm, + ) -> Result { + let data_contract: DataContract = data_contract.clone().into(); + let mut value = raw_object.with_serde_to_json_value()?; + let document_type = value + .get_string(document::property_names::DOCUMENT_TYPE) + .with_js_error()?; + + let (identifier_paths, _) = data_contract + .get_identifiers_and_binary_paths(document_type) + .with_js_error()?; + // Allow to fail as it could be a Buffer or Identifier + let _ = value.replace_identifier_paths( + identifier_paths + .into_iter() + .chain(document_create_transition::IDENTIFIER_FIELDS), + ReplaceWith::Bytes, + ); + let transition = + DocumentCreateTransition::from_raw_object(value, data_contract).with_js_error()?; + + Ok(transition.into()) + } + + // DocumentCreateTransition + #[wasm_bindgen(js_name=getEntropy)] + pub fn entropy(&self) -> Vec { + self.inner.entropy.to_vec() + } + + #[wasm_bindgen(js_name=getCreatedAt)] + pub fn created_at(&self) -> Option { + self.inner.created_at + } + + #[wasm_bindgen(js_name=getUpdatedAt)] + pub fn updated_at(&self) -> Option { + self.inner.updated_at + } + + #[wasm_bindgen(js_name=getRevision)] + pub fn revision(&self) -> u32 { + document_transition::INITIAL_REVISION + } + + // AbstractDocumentTransitionMethods + #[wasm_bindgen(js_name=getId)] + pub fn id(&self) -> IdentifierWrapper { + self.inner.base.id.clone().into() + } + + #[wasm_bindgen(js_name=getType)] + pub fn document_type(&self) -> String { + self.inner.base.document_type.clone() + } + #[wasm_bindgen(js_name=getAction)] pub fn action(&self) -> u8 { self.inner.base.action as u8 } + #[wasm_bindgen(js_name=getDataContract)] + pub fn data_contract(&self) -> DataContractWasm { + self.inner.base.data_contract.clone().into() + } + + #[wasm_bindgen(js_name=getDataContractId)] + pub fn data_contract_id(&self) -> IdentifierWrapper { + self.inner.base.data_contract.id.into() + } + + #[wasm_bindgen] + pub fn get(&self, path: String) -> Result { + let document_data = if let Some(ref data) = self.inner.data { + data + } else { + return Ok(JsValue::undefined()); + }; + + let mut value = if let Ok(value) = document_data.get_value(&path) { + value.to_owned() + } else { + return Ok(JsValue::undefined()); + }; + + match self.get_binary_type_of_path(&path) { + BinaryType::Buffer => { + let bytes: Vec = serde_json::from_value(value).unwrap(); + let buffer = Buffer::from_bytes(&bytes); + return Ok(buffer.into()); + } + BinaryType::Identifier => { + let bytes: Vec = serde_json::from_value(value).unwrap(); + let id = >::from( + Identifier::from_bytes(&bytes).with_js_error()?, + ); + return Ok(id.into()); + } + BinaryType::None => { + // Do nothing. If is 'None' it means that binary may contain binary data + // or may not captain it at all + } + } + + let js_value = value.serialize(&serde_wasm_bindgen::Serializer::json_compatible())?; + let (identifier_paths, binary_paths) = self + .inner + .base + .data_contract + .get_identifiers_and_binary_paths(&self.inner.base.document_type) + .with_js_error()?; + + for property_path in identifier_paths { + if property_path.starts_with(&path) { + let (_, suffix) = property_path.split_at(path.len() + 1); + + if value.get_value(suffix).is_ok() { + // unwrap allowed because the line above + let bytes = value.remove_path_into::>(suffix).unwrap(); + let id = >::from( + Identifier::from_bytes(&bytes).unwrap(), + ); + lodash_set(&js_value, suffix, id.into()); + } + } + } + + for property_path in binary_paths { + if property_path.starts_with(&path) { + let (_, suffix) = property_path.split_at(path.len() + 1); + + if value.get_value(suffix).is_ok() { + // unwrap allowed because the line above + let bytes = value.remove_path_into::>(suffix).unwrap(); + let buffer = Buffer::from_bytes(&bytes); + lodash_set(&js_value, suffix, buffer.into()); + } + } + } + + Ok(js_value) + } + + // DocumentTransitionObjectLike #[wasm_bindgen(js_name=toObject)] - pub fn to_object(&self) -> Result { - let mut value = self.inner.to_object().with_js_error()?; + pub fn to_object(&self, options: &JsValue) -> Result { + let (identifiers_paths, binary_paths) = self + .inner + .base + .data_contract + .get_identifiers_and_binary_paths(&self.inner.base.document_type) + .with_js_error()?; + + to_object( + &self.inner, + options, + identifiers_paths + .into_iter() + .chain(document_create_transition::IDENTIFIER_FIELDS), + binary_paths + .into_iter() + .chain(document_create_transition::BINARY_FIELDS), + ) + } + + #[wasm_bindgen(js_name=toJSON)] + pub fn to_json(&self) -> Result { + let value = self.inner.to_json().with_js_error()?; let serializer = serde_wasm_bindgen::Serializer::json_compatible(); let js_value = value.serialize(&serializer)?; + Ok(js_value) + } - let (identifiers_paths, binary_paths) = self + // AbstractDataDocumentTransition + #[wasm_bindgen(js_name=getData)] + pub fn get_data(&self) -> Result { + let data = if let Some(ref data) = self.inner.data { + data + } else { + return Ok(JsValue::undefined()); + }; + + let js_value = data.serialize(&serde_wasm_bindgen::Serializer::json_compatible())?; + let (identifier_paths, binary_paths) = self .inner .base .data_contract .get_identifiers_and_binary_paths(&self.inner.base.document_type) .with_js_error()?; - for path in identifiers_paths - .into_iter() - .chain(document_create_transition::IDENTIFIER_FIELDS) - { - if let Ok(bytes) = value.remove_path_into::>(path) { - let id = IdentifierWrapper::new(bytes)?; + for path in identifier_paths { + if let Ok(value) = data.get_value(path) { + let bytes: Vec = serde_json::from_value(value.to_owned()).with_js_error()?; + let id = >::from( + Identifier::from_bytes(&bytes).unwrap(), + ); lodash_set(&js_value, path, id.into()); } } - - for path in binary_paths - .into_iter() - .chain(document_create_transition::BINARY_FIELDS) - { - if let Ok(bytes) = value.remove_path_into::>(path) { + for path in binary_paths { + if let Ok(value) = data.get_value(path) { + let bytes: Vec = serde_json::from_value(value.to_owned()).with_js_error()?; let buffer = Buffer::from_bytes(&bytes); lodash_set(&js_value, path, buffer.into()); } @@ -66,3 +250,23 @@ impl DocumentCreateTransitionWasm { Ok(js_value) } } + +impl DocumentCreateTransitionWasm { + fn get_binary_type_of_path(&self, path: &String) -> BinaryType { + let maybe_binary_properties = self + .inner + .base + .data_contract + .get_binary_properties(&self.inner.base.document_type); + + if let Ok(binary_properties) = maybe_binary_properties { + if let Some(data) = binary_properties.get(path) { + if data.is_type_of_identifier() { + return BinaryType::Identifier; + } + return BinaryType::Buffer; + } + } + BinaryType::None + } +} diff --git a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_delete_transition.rs b/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_delete_transition.rs index 181c541e440..baa04f072b1 100644 --- a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_delete_transition.rs +++ b/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_delete_transition.rs @@ -1,13 +1,13 @@ -use dpp::{ - document::document_transition::{ - document_delete_transition, DocumentDeleteTransition, DocumentTransitionObjectLike, - }, - util::json_value::JsonValueExt, +use dpp::document::document_transition::{ + document_delete_transition, DocumentDeleteTransition, DocumentTransitionObjectLike, }; use serde::Serialize; use wasm_bindgen::prelude::*; -use crate::{identifier::IdentifierWrapper, lodash::lodash_set, utils::WithJsError}; +use crate::{ + document_batch_transition::document_transition::to_object, identifier::IdentifierWrapper, + utils::WithJsError, DataContractWasm, +}; #[wasm_bindgen(js_name=DocumentDeleteTransition)] #[derive(Debug, Clone)] @@ -29,18 +29,47 @@ impl DocumentDeleteTransitionWasm { } #[wasm_bindgen(js_name=toObject)] - pub fn to_object(&self) -> Result { - let mut value = self.inner.to_object().with_js_error()?; + pub fn to_object(&self, options: &JsValue) -> Result { + to_object( + &self.inner, + options, + document_delete_transition::IDENTIFIER_FIELDS, + [], + ) + } + + #[wasm_bindgen(js_name=toJSON)] + pub fn to_json(&self) -> Result { + let value = self.inner.to_json().with_js_error()?; let serializer = serde_wasm_bindgen::Serializer::json_compatible(); let js_value = value.serialize(&serializer)?; + Ok(js_value) + } - for field in document_delete_transition::IDENTIFIER_FIELDS { - if let Ok(bytes) = value.remove_path_into::>(field) { - let id = IdentifierWrapper::new(bytes)?; - lodash_set(&js_value, field, id.into()); - } - } + // AbstractDocumentTransition + #[wasm_bindgen(js_name=getId)] + pub fn id(&self) -> IdentifierWrapper { + self.inner.base.id.clone().into() + } - Ok(js_value) + #[wasm_bindgen(js_name=getType)] + pub fn document_type(&self) -> String { + self.inner.base.document_type.clone() + } + + #[wasm_bindgen(js_name=getDataContract)] + pub fn data_contract(&self) -> DataContractWasm { + self.inner.base.data_contract.clone().into() + } + + #[wasm_bindgen(js_name=getDataContractId)] + pub fn data_contract_id(&self) -> IdentifierWrapper { + self.inner.base.data_contract.id.clone().into() + } + + #[wasm_bindgen(js_name=get)] + pub fn get(&self, path: String) -> Result { + let _ = path; + Ok(JsValue::undefined()) } } diff --git a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_replace_transition.rs b/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_replace_transition.rs index 1634f870e77..529b19cf5d9 100644 --- a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_replace_transition.rs +++ b/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_replace_transition.rs @@ -1,17 +1,32 @@ +use std::convert; + use dpp::{ - document::document_transition::{ - document_replace_transition, DocumentReplaceTransition, DocumentTransitionObjectLike, + document::{ + self, + document_transition::{ + document_create_transition, document_replace_transition, DocumentReplaceTransition, + DocumentTransitionObjectLike, + }, + }, + prelude::{DataContract, Identifier}, + util::{ + json_schema::JsonSchemaExt, + json_value::{JsonValueExt, ReplaceWith}, }, - util::json_value::JsonValueExt, }; use serde::Serialize; use wasm_bindgen::prelude::*; use crate::{ - buffer::Buffer, identifier::IdentifierWrapper, lodash::lodash_set, utils::WithJsError, + buffer::Buffer, + document_batch_transition::document_transition::to_object, + identifier::IdentifierWrapper, + lodash::lodash_set, + utils::{ToSerdeJSONExt, WithJsError}, + BinaryType, DataContractWasm, }; -#[wasm_bindgen(js_name=DocumentTransition)] +#[wasm_bindgen(js_name=DocumentReplaceTransition)] #[derive(Debug, Clone)] pub struct DocumentReplaceTransitionWasm { inner: DocumentReplaceTransition, @@ -23,38 +38,106 @@ impl From for DocumentReplaceTransitionWasm { } } -#[wasm_bindgen(js_class=DocumentTransition)] +#[wasm_bindgen(js_class=DocumentReplaceTransition)] impl DocumentReplaceTransitionWasm { + #[wasm_bindgen(constructor)] + pub fn from_raw_object( + raw_object: JsValue, + data_contract: &DataContractWasm, + ) -> Result { + let data_contract: DataContract = data_contract.clone().into(); + let mut value = raw_object.with_serde_to_json_value()?; + let document_type = value + .get_string(document::property_names::DOCUMENT_TYPE) + .with_js_error()?; + + let (identifier_paths, _) = data_contract + .get_identifiers_and_binary_paths(document_type) + .with_js_error()?; + // Allow to fail as it could be a Buffer or Identifier + let _ = value.replace_identifier_paths( + identifier_paths + .into_iter() + .chain(document_create_transition::IDENTIFIER_FIELDS), + ReplaceWith::Bytes, + ); + let transition = + DocumentReplaceTransition::from_raw_object(value, data_contract).with_js_error()?; + + Ok(transition.into()) + } + #[wasm_bindgen(js_name=getAction)] pub fn action(&self) -> u8 { self.inner.base.action as u8 } + #[wasm_bindgen(js_name=getRevision)] + pub fn revision(&self) -> u32 { + self.inner.revision + } + + #[wasm_bindgen(js_name=getUpdatedAt)] + pub fn updated_at(&self) -> Option { + self.inner.updated_at + } + #[wasm_bindgen(js_name=toObject)] - pub fn to_object(&self) -> Result { - let mut value = self.inner.to_object().with_js_error()?; + pub fn to_object(&self, options: &JsValue) -> Result { + let (identifiers_paths, binary_paths) = self + .inner + .base + .data_contract + .get_identifiers_and_binary_paths(&self.inner.base.document_type) + .with_js_error()?; + + to_object( + &self.inner, + options, + identifiers_paths + .into_iter() + .chain(document_replace_transition::IDENTIFIER_FIELDS), + binary_paths, + ) + } + + #[wasm_bindgen(js_name=toJSON)] + pub fn to_json(&self) -> Result { + let value = self.inner.to_json().with_js_error()?; let serializer = serde_wasm_bindgen::Serializer::json_compatible(); let js_value = value.serialize(&serializer)?; + Ok(js_value) + } - let (identifiers_paths, binary_paths) = self + // AbstractDataDocumentTransition + #[wasm_bindgen(js_name=getData)] + pub fn get_data(&self) -> Result { + let data = if let Some(ref data) = self.inner.data { + data + } else { + return Ok(JsValue::undefined()); + }; + + let js_value = data.serialize(&serde_wasm_bindgen::Serializer::json_compatible())?; + let (identifier_paths, binary_paths) = self .inner .base .data_contract .get_identifiers_and_binary_paths(&self.inner.base.document_type) .with_js_error()?; - for path in identifiers_paths - .into_iter() - .chain(document_replace_transition::IDENTIFIER_FIELDS) - { - if let Ok(bytes) = value.remove_path_into::>(path) { - let id = IdentifierWrapper::new(bytes)?; + for path in identifier_paths { + if let Ok(value) = data.get_value(path) { + let bytes: Vec = serde_json::from_value(value.to_owned()).with_js_error()?; + let id = >::from( + Identifier::from_bytes(&bytes).unwrap(), + ); lodash_set(&js_value, path, id.into()); } } - - for path in binary_paths.into_iter() { - if let Ok(bytes) = value.remove_path_into::>(path) { + for path in binary_paths { + if let Ok(value) = data.get_value(path) { + let bytes: Vec = serde_json::from_value(value.to_owned()).with_js_error()?; let buffer = Buffer::from_bytes(&bytes); lodash_set(&js_value, path, buffer.into()); } @@ -62,4 +145,117 @@ impl DocumentReplaceTransitionWasm { Ok(js_value) } + + // AbstractDocumentTransition + #[wasm_bindgen(js_name=getId)] + pub fn id(&self) -> IdentifierWrapper { + self.inner.base.id.clone().into() + } + + #[wasm_bindgen(js_name=getType)] + pub fn document_type(&self) -> String { + self.inner.base.document_type.clone() + } + + #[wasm_bindgen(js_name=getDataContract)] + pub fn data_contract(&self) -> DataContractWasm { + self.inner.base.data_contract.clone().into() + } + + #[wasm_bindgen(js_name=getDataContractId)] + pub fn data_contract_id(&self) -> IdentifierWrapper { + self.inner.base.data_contract.id.clone().into() + } + + #[wasm_bindgen(js_name=get)] + pub fn get(&self, path: String) -> Result { + let document_data = if let Some(ref data) = self.inner.data { + data + } else { + return Ok(JsValue::undefined()); + }; + + let mut value = if let Ok(value) = document_data.get_value(&path) { + value.to_owned() + } else { + return Ok(JsValue::undefined()); + }; + + match self.get_binary_type_of_path(&path) { + BinaryType::Buffer => { + let bytes: Vec = serde_json::from_value(value).unwrap(); + let buffer = Buffer::from_bytes(&bytes); + return Ok(buffer.into()); + } + BinaryType::Identifier => { + let bytes: Vec = serde_json::from_value(value).unwrap(); + let id = >::from( + Identifier::from_bytes(&bytes).unwrap(), + ); + return Ok(id.into()); + } + BinaryType::None => { + // Do nothing. If is 'None' it means that binary may contain binary data + // or may not captain it at all + } + } + + let js_value = value.serialize(&serde_wasm_bindgen::Serializer::json_compatible())?; + let (identifier_paths, binary_paths) = self + .inner + .base + .data_contract + .get_identifiers_and_binary_paths(&self.inner.base.document_type) + .with_js_error()?; + + for property_path in identifier_paths { + if property_path.starts_with(&path) { + let (_, suffix) = property_path.split_at(path.len() + 1); + + if value.get_value(suffix).is_ok() { + // unwrap allowed because the line above + let bytes = value.remove_path_into::>(suffix).unwrap(); + let id = >::from( + Identifier::from_bytes(&bytes).unwrap(), + ); + lodash_set(&js_value, suffix, id.into()); + } + } + } + + for property_path in binary_paths { + if property_path.starts_with(&path) { + let (_, suffix) = property_path.split_at(path.len() + 1); + + if value.get_value(suffix).is_ok() { + // unwrap allowed because the line above + let bytes = value.remove_path_into::>(suffix).unwrap(); + let buffer = Buffer::from_bytes(&bytes); + lodash_set(&js_value, suffix, buffer.into()); + } + } + } + + Ok(js_value) + } +} + +impl DocumentReplaceTransitionWasm { + fn get_binary_type_of_path(&self, path: &String) -> BinaryType { + let maybe_binary_properties = self + .inner + .base + .data_contract + .get_binary_properties(&self.inner.base.document_type); + + if let Ok(binary_properties) = maybe_binary_properties { + if let Some(data) = binary_properties.get(path) { + if data.is_type_of_identifier() { + return BinaryType::Identifier; + } + return BinaryType::Buffer; + } + } + BinaryType::None + } } diff --git a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_update_transition.rs b/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_update_transition.rs deleted file mode 100644 index 1dd86bcfaed..00000000000 --- a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_update_transition.rs +++ /dev/null @@ -1,22 +0,0 @@ -use dpp::document::document_transition::DocumentReplaceTransition; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen(js_name=DocumentTransition)] -#[derive(Debug, Clone)] -pub struct DocumentReplaceTransitionWasm { - inner: DocumentReplaceTransition, -} - -impl From for DocumentReplaceTransitionWasm { - fn from(v: DocumentReplaceTransition) -> Self { - Self { inner: v } - } -} - -#[wasm_bindgen(js_class=DocumentTransition)] -impl DocumentReplaceTransitionWasm { - #[wasm_bindgen(js_name=getAction)] - pub fn action(&self) -> u8 { - self.inner.base.action as u8 - } -} diff --git a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/mod.rs b/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/mod.rs index f015fa31954..7d0cd7b4f28 100644 --- a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/mod.rs +++ b/packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/mod.rs @@ -9,14 +9,17 @@ pub use document_replace_transition::*; use dpp::{ document::document_transition::{DocumentTransitionExt, DocumentTransitionObjectLike}, prelude::{DocumentTransition, Identifier}, - util::json_schema::JsonSchemaExt, + util::{json_schema::JsonSchemaExt, json_value::JsonValueExt}, }; use serde::Serialize; use wasm_bindgen::prelude::*; use crate::{ - buffer::Buffer, identifier::IdentifierWrapper, utils::WithJsError, with_js_error, BinaryType, - DataContractWasm, + buffer::Buffer, + identifier::IdentifierWrapper, + lodash::lodash_set, + utils::{ToSerdeJSONExt, WithJsError}, + with_js_error, BinaryType, ConversionOptions, DataContractWasm, }; #[wasm_bindgen] @@ -81,18 +84,16 @@ impl DocumentTransitionWasm { } #[wasm_bindgen(js_name=getObject)] - pub fn to_object(&self, _options: &JsValue) -> Result { - // TODO options?? - + pub fn to_object(&self, options: &JsValue) -> Result { match self.0 { DocumentTransition::Create(ref t) => { - DocumentCreateTransitionWasm::from(t.to_owned()).to_object() + DocumentCreateTransitionWasm::from(t.to_owned()).to_object(options) } DocumentTransition::Replace(ref t) => { - DocumentReplaceTransitionWasm::from(t.to_owned()).to_object() + DocumentReplaceTransitionWasm::from(t.to_owned()).to_object(options) } DocumentTransition::Delete(ref t) => { - DocumentDeleteTransitionWasm::from(t.to_owned()).to_object() + DocumentDeleteTransitionWasm::from(t.to_owned()).to_object(options) } } } @@ -142,3 +143,42 @@ pub fn from_document_transition_to_js_value(document_transition: DocumentTransit } } } + +pub(crate) fn to_object<'a>( + data: &impl DocumentTransitionObjectLike, + options: &JsValue, + identifiers_paths: impl IntoIterator, + binary_paths: impl IntoIterator, +) -> Result { + let options: ConversionOptions = if options.is_object() { + let raw_options = options.with_serde_to_json_value()?; + serde_json::from_value(raw_options).with_js_error()? + } else { + Default::default() + }; + + let mut value = data.to_object().with_js_error()?; + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + let js_value = value.serialize(&serializer)?; + + for path in identifiers_paths.into_iter() { + if let Ok(bytes) = value.remove_path_into::>(path) { + if !options.skip_identifiers_conversion { + let buffer = Buffer::from_bytes(&bytes); + lodash_set(&js_value, path, buffer.into()); + } else { + let id = IdentifierWrapper::new(bytes)?; + lodash_set(&js_value, path, id.into()); + } + } + } + + for path in binary_paths.into_iter() { + if let Ok(bytes) = value.remove_path_into::>(path) { + let buffer = Buffer::from_bytes(&bytes); + lodash_set(&js_value, path, buffer.into()); + } + } + + Ok(js_value) +} diff --git a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/mod.rs b/packages/wasm-dpp/src/document/state_transition/document_batch_transition/mod.rs index 6792c1bfdd3..f5fb53f967e 100644 --- a/packages/wasm-dpp/src/document/state_transition/document_batch_transition/mod.rs +++ b/packages/wasm-dpp/src/document/state_transition/document_batch_transition/mod.rs @@ -12,7 +12,7 @@ use dpp::{ util::json_value::JsonValueExt, }; use js_sys::{Array, Reflect}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; use crate::{ @@ -39,6 +39,15 @@ pub struct DocumentsContainer { delete: Vec, } +#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy)] +#[serde(rename_all = "camelCase")] +pub struct ToObjectOptions { + #[serde(default)] + skip_signature: bool, + #[serde(default)] + skip_identifiers_conversion: bool, +} + #[wasm_bindgen(js_class=DocumentsContainer)] impl DocumentsContainer { #[wasm_bindgen(constructor)] @@ -154,15 +163,15 @@ impl DocumentsBatchTransitionWASM { } #[wasm_bindgen(js_name=toObject)] - pub fn to_object(&self, options: &JsValue) -> Result { - let skip_signature = if options.is_object() { - let options = options.with_serde_to_json_value()?; - options.get_bool("skipSignature").unwrap_or_default() + pub fn to_object(&self, js_options: &JsValue) -> Result { + let options: ToObjectOptions = if js_options.is_object() { + let raw_options = js_options.with_serde_to_json_value()?; + serde_json::from_value(raw_options).with_js_error()? } else { - false + Default::default() }; - let mut value = self.0.to_object(skip_signature).with_js_error()?; + let mut value = self.0.to_object(options.skip_signature).with_js_error()?; let serializer = serde_wasm_bindgen::Serializer::json_compatible(); let js_value = value.serialize(&serializer)?; @@ -170,7 +179,7 @@ impl DocumentsBatchTransitionWASM { let transitions = Array::new(); for transition in self.0.transitions.iter() { let js_value = - DocumentTransitionWasm::from(transition.to_owned()).to_object(options)?; + DocumentTransitionWasm::from(transition.to_owned()).to_object(js_options)?; transitions.push(&js_value); } // Replace the whole collection of transitions @@ -189,12 +198,17 @@ impl DocumentsBatchTransitionWASM { } for path in DocumentsBatchTransition::identifiers_property_paths() { if let Ok(bytes) = value.remove_path_into::>(path) { - let id = IdentifierWrapper::new(bytes)?; - lodash_set(&js_value, path, id.into()); + if !options.skip_identifiers_conversion { + let buffer = Buffer::from_bytes(&bytes); + lodash_set(&js_value, path, buffer.into()); + } else { + let id = IdentifierWrapper::new(bytes)?; + lodash_set(&js_value, path, id.into()); + } } } - if value.get(property_names::SIGNATURE).is_none() && !skip_signature { + if value.get(property_names::SIGNATURE).is_none() && !options.skip_signature { js_sys::Reflect::set( &js_value, &property_names::SIGNATURE.into(), diff --git a/packages/wasm-dpp/src/identity/validation/public_keys_validator.rs b/packages/wasm-dpp/src/identity/validation/public_keys_validator.rs index ae94404466a..2d284c89f45 100644 --- a/packages/wasm-dpp/src/identity/validation/public_keys_validator.rs +++ b/packages/wasm-dpp/src/identity/validation/public_keys_validator.rs @@ -38,7 +38,6 @@ impl PublicKeysValidatorWasm { .public_key_validator .validate_keys(&raw_public_keys) .map_err(|e| JsValue::from(e.to_string()))?; - Ok(validation_result.map(|_| JsValue::undefined()).into()) } @@ -53,7 +52,6 @@ impl PublicKeysValidatorWasm { .public_key_validator .validate_public_key_structure(&pk_serde_json) .map_err(|e| JsValue::from(e.to_string()))?; - Ok(validation_result.map(|_| JsValue::undefined()).into()) } @@ -68,7 +66,6 @@ impl PublicKeysValidatorWasm { .public_key_in_state_transition_validator .validate_keys(&raw_public_keys) .map_err(|e| JsValue::from(e.to_string()))?; - Ok(validation_result.map(|_| JsValue::undefined()).into()) } } diff --git a/packages/wasm-dpp/src/state_repository.rs b/packages/wasm-dpp/src/state_repository.rs index 61c47cc3fb9..0c151a07e4d 100644 --- a/packages/wasm-dpp/src/state_repository.rs +++ b/packages/wasm-dpp/src/state_repository.rs @@ -235,7 +235,7 @@ impl StateRepositoryLike for ExternalStateRepositoryLikeWrapper { .await .map_err(from_js_error)?; - if maybe_data_contract.is_undefined() { + if maybe_data_contract.is_undefined() || maybe_data_contract.is_null() { return Ok(None); } diff --git a/packages/wasm-dpp/src/utils.rs b/packages/wasm-dpp/src/utils.rs index 668cfd43a8a..79b09255533 100644 --- a/packages/wasm-dpp/src/utils.rs +++ b/packages/wasm-dpp/src/utils.rs @@ -6,8 +6,7 @@ use dpp::{ use js_sys::Function; use serde::de::DeserializeOwned; use serde_json::Value; -use wasm_bindgen::convert::RefFromWasmAbi; -use wasm_bindgen::prelude::*; +use wasm_bindgen::{convert::RefFromWasmAbi, prelude::*}; use crate::errors::{from_dpp_err, RustConversionError}; diff --git a/packages/wasm-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js b/packages/wasm-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js index 1b36479fad9..76ed727c4dc 100644 --- a/packages/wasm-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js +++ b/packages/wasm-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js @@ -5,51 +5,89 @@ const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createSta const getDocumentsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentsFixture'); const getDataContractFixture = require('@dashevo/dpp/lib/test/fixtures/getDataContractFixture'); -const ValidationResult = require('@dashevo/dpp/lib/validation/ValidationResult'); +const ValidationResultJs = require('@dashevo/dpp/lib/validation/ValidationResult'); -const MissingDataContractIdError = require('@dashevo/dpp/lib/errors/consensus/basic/document/MissingDataContractIdError'); -const DataContractNotPresentError = require('@dashevo/dpp/lib/errors/consensus/basic/document/DataContractNotPresentError'); +const MissingDataContractIdErrorJs = require('@dashevo/dpp/lib/errors/consensus/basic/document/MissingDataContractIdError'); +const DataContractNotPresentErrorJs = require('@dashevo/dpp/lib/errors/consensus/basic/document/DataContractNotPresentError'); const { expectValidationError } = require('@dashevo/dpp/lib/test/expect/expectError'); +const { default: loadWasmDpp } = require('../../../dist'); + +let ValidationResult; +let DataContract; + describe('fetchAndValidateDataContractFactory', () => { + let stateRepositoryMockJs; let stateRepositoryMock; + let fetchAndValidateDataContractJs; let fetchAndValidateDataContract; let rawDocument; + beforeEach(async () => { + ({ + DataContract, + fetchAndValidateDataContract, + ValidationResult, + } = await loadWasmDpp()); + }); + beforeEach(function beforeEach() { - const dataContract = getDataContractFixture(); + const dataContractJs = getDataContractFixture(); + const dataContract = DataContract.fromBuffer(dataContractJs.toBuffer()); - const [document] = getDocumentsFixture(dataContract); + const [document] = getDocumentsFixture(dataContractJs); rawDocument = document.toObject(); - stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMockJs = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMockJs.fetchDataContract.resolves(dataContractJs); + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); stateRepositoryMock.fetchDataContract.resolves(dataContract); - fetchAndValidateDataContract = fetchAndValidateDataContractFactory( - stateRepositoryMock, + fetchAndValidateDataContractJs = fetchAndValidateDataContractFactory( + stateRepositoryMockJs, ); }); it('should return with invalid result if $dataContractId is not present', async () => { delete rawDocument.$dataContractId; - const result = await fetchAndValidateDataContract(rawDocument); + const result = await fetchAndValidateDataContractJs(rawDocument); - expectValidationError(result, MissingDataContractIdError); + expectValidationError(result, MissingDataContractIdErrorJs); const [error] = result.getErrors(); expect(error.getCode()).to.equal(1025); }); + it('should return with invalid result if $dataContractId is not present - Rust', async () => { + delete rawDocument.$dataContractId; + + const result = await fetchAndValidateDataContract(stateRepositoryMock, rawDocument); + + const [error] = result.getErrors(); + expect(error.getCode()).to.equal(1025); + }); + it('should return with invalid result if Data Contract is not present', async () => { - stateRepositoryMock.fetchDataContract.resolves(null); + stateRepositoryMockJs.fetchDataContract.resolves(null); - const result = await fetchAndValidateDataContract(rawDocument); + const result = await fetchAndValidateDataContractJs(rawDocument); - expectValidationError(result, DataContractNotPresentError); + expectValidationError(result, DataContractNotPresentErrorJs); + + const [error] = result.getErrors(); + + expect(error.getCode()).to.equal(1018); + expect(error.getDataContractId()).to.deep.equal(rawDocument.$dataContractId); + }); + + it('should return with invalid result if Data Contract is not present - Rust', async () => { + stateRepositoryMock.fetchDataContract.resolves(null); + + const result = await fetchAndValidateDataContract(stateRepositoryMock, rawDocument); const [error] = result.getErrors(); @@ -58,7 +96,14 @@ describe('fetchAndValidateDataContractFactory', () => { }); it('should return valid result', async () => { - const result = await fetchAndValidateDataContract(rawDocument); + const result = await fetchAndValidateDataContractJs(rawDocument); + + expect(result).to.be.an.instanceOf(ValidationResultJs); + expect(result.isValid()).to.be.true(); + }); + + it('should return valid result - Rust', async () => { + const result = await fetchAndValidateDataContract(stateRepositoryMock, rawDocument); expect(result).to.be.an.instanceOf(ValidationResult); expect(result.isValid()).to.be.true(); diff --git a/packages/wasm-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/DocumentsBatchTransition.spec.js b/packages/wasm-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/DocumentsBatchTransition.spec.js index 329f56111e8..20741755685 100644 --- a/packages/wasm-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/DocumentsBatchTransition.spec.js +++ b/packages/wasm-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/DocumentsBatchTransition.spec.js @@ -4,11 +4,9 @@ const stateTransitionTypes = require('@dashevo/dpp/lib/stateTransition/stateTran const createDPPMock = require('@dashevo/dpp/lib/test/mocks/createDPPMock'); const protocolVersion = require('@dashevo/dpp/lib/version/protocolVersion'); const DocumentFactoryJs = require('@dashevo/dpp/lib/document/DocumentFactory'); -const lodash = require('lodash'); const { default: loadWasmDpp } = require('../../../../../dist'); const newDocumentsContainer = require('../../../../../lib/test/utils/newDocumentsContainer'); -let Identifier; let DocumentFactory; let DataContract; let Document; @@ -26,7 +24,7 @@ describe('DocumentsBatchTransition', () => { beforeEach(async () => { ({ - Identifier, ProtocolVersionValidator, DocumentValidator, DocumentFactory, DataContract, + ProtocolVersionValidator, DocumentValidator, DocumentFactory, DataContract, Document, } = await loadWasmDpp()); }); @@ -143,14 +141,8 @@ describe('DocumentsBatchTransition', () => { it('should return State Transition as plain object - Rust', () => { const rawObject = stateTransition.toObject(); const rawObjectJs = stateTransitionJs.toObject(); - /* eslint-disable */ - const rawObjectWithBuffers = lodash.cloneDeepWith(rawObject, (value) => { - if (value instanceof Identifier) { - return value.toBuffer(); - } - }); - expect(rawObjectWithBuffers).to.deep.equal(rawObjectJs); + expect(rawObject).to.deep.equal(rawObjectJs); }); }); diff --git a/packages/wasm-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/documentTransition/DocumentCreateTransition.spec.js b/packages/wasm-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/documentTransition/DocumentCreateTransition.spec.js index c00aa3a223a..ac0036beefa 100644 --- a/packages/wasm-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/documentTransition/DocumentCreateTransition.spec.js +++ b/packages/wasm-dpp/test/unit/document/stateTransition/DocumetsBatchTransition/documentTransition/DocumentCreateTransition.spec.js @@ -1,24 +1,54 @@ const getDocumentTransitionsFixture = require('@dashevo/dpp/lib/test/fixtures/getDocumentTransitionsFixture'); +const { default: loadWasmDpp } = require('../../../../../../dist'); + +let DataContract; +let DocumentCreateTransition; describe('DocumentCreateTransition', () => { + let documentTransitionJs; let documentTransition; + beforeEach(async () => { + ({ + DataContract, DocumentCreateTransition, + } = await loadWasmDpp()); + }); + beforeEach(() => { - [documentTransition] = getDocumentTransitionsFixture(); + [documentTransitionJs] = getDocumentTransitionsFixture(); + const dataContractJs = documentTransitionJs.dataContract; + const dataContract = DataContract.fromBuffer(dataContractJs.toBuffer()); + + documentTransition = new DocumentCreateTransition(documentTransitionJs.toObject(), + dataContract); }); describe('toJSON', () => { it('should return json representation', () => { + const jsonDocumentTransition = documentTransitionJs.toJSON(); + + expect(jsonDocumentTransition).to.deep.equal({ + $id: documentTransitionJs.getId().toString(), + $type: documentTransitionJs.getType(), + $action: documentTransitionJs.getAction(), + $dataContractId: documentTransitionJs.getDataContractId().toString(), + $entropy: documentTransitionJs.getEntropy().toString('base64'), + $createdAt: documentTransitionJs.getCreatedAt().getTime(), + name: documentTransitionJs.getData().name, + }); + }); + + it('should return json representation - Rust', () => { const jsonDocumentTransition = documentTransition.toJSON(); expect(jsonDocumentTransition).to.deep.equal({ - $id: documentTransition.getId().toString(), - $type: documentTransition.getType(), - $action: documentTransition.getAction(), - $dataContractId: documentTransition.getDataContractId().toString(), - $entropy: documentTransition.getEntropy().toString('base64'), - $createdAt: documentTransition.getCreatedAt().getTime(), - name: documentTransition.getData().name, + $id: documentTransitionJs.getId().toString(), + $type: documentTransitionJs.getType(), + $action: documentTransitionJs.getAction(), + $dataContractId: documentTransitionJs.getDataContractId().toString(), + $entropy: documentTransitionJs.getEntropy().toString('base64'), + $createdAt: documentTransitionJs.getCreatedAt().getTime(), + name: documentTransitionJs.getData().name, }); }); });