From 86336887ee2d1077b34da2805dcd5bb659a92898 Mon Sep 17 00:00:00 2001 From: Pawel Iwan Date: Thu, 12 Jan 2023 10:19:36 +0100 Subject: [PATCH 1/7] feat(dpp): wasm bindings for Document Transitions --- .../state/validatePublicKeysState.js | 4 +- .../src/validation/validation_result.rs | 10 + packages/wasm-dpp/Cargo.toml | 2 + packages/wasm-dpp/src/conversion.rs | 8 + .../validation.rs | 6 +- .../validation.rs | 13 +- .../data_contract_factory.rs | 6 +- .../fetch_and_validate_data_contract.rs | 40 +++ packages/wasm-dpp/src/document/mod.rs | 38 ++- .../document_create_transition.rs | 236 ++++++++++++++++-- .../document_delete_transition.rs | 59 +++-- .../document_replace_transition.rs | 228 +++++++++++++++-- .../document_update_transition.rs | 22 -- .../document_transition/mod.rs | 57 ++++- .../document_batch_transition/mod.rs | 36 ++- .../wasm-dpp/src/identity/identity_facade.rs | 6 +- .../validation/public_keys_validator.rs | 21 +- packages/wasm-dpp/src/lib.rs | 1 + packages/wasm-dpp/src/utils.rs | 4 +- .../src/validation/validation_result.rs | 18 +- ...etchAndValidateDataContractFactory.spec.js | 75 ++++-- .../DocumentsBatchTransition.spec.js | 12 +- .../DocumentCreateTransition.spec.js | 46 +++- 23 files changed, 778 insertions(+), 170 deletions(-) create mode 100644 packages/wasm-dpp/src/conversion.rs create mode 100644 packages/wasm-dpp/src/document/fetch_and_validate_data_contract.rs delete mode 100644 packages/wasm-dpp/src/document/state_transition/document_batch_transition/document_transition/document_update_transition.rs diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.js b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.js index 3416355f2a9..ea75fd4677a 100644 --- a/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.js +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.js @@ -21,10 +21,8 @@ const MaxIdentityPublicKeyLimitReachedError = require( * @param {RawIdentityPublicKey[]} rawPublicKeys * * @return {ValidationResult} - */ -function validatePublicKeysState(rawPublicKeys) { + */ function validatePublicKeysState(rawPublicKeys) { const result = new ValidationResult(); - if (rawPublicKeys.length > identitySchema.properties.publicKeys.maxItems) { result.addError( new MaxIdentityPublicKeyLimitReachedError(identitySchema.properties.publicKeys.maxItems), diff --git a/packages/rs-dpp/src/validation/validation_result.rs b/packages/rs-dpp/src/validation/validation_result.rs index a5b9ab0342e..386518a3312 100644 --- a/packages/rs-dpp/src/validation/validation_result.rs +++ b/packages/rs-dpp/src/validation/validation_result.rs @@ -17,6 +17,16 @@ impl ValidationResult { } } + pub fn map(self, f: F) -> ValidationResult + where + F: FnOnce(TData) -> U, + { + ValidationResult { + errors: self.errors, + data: self.data.map(f), + } + } + pub fn add_error(&mut self, error: T) where T: Into, diff --git a/packages/wasm-dpp/Cargo.toml b/packages/wasm-dpp/Cargo.toml index 8780683152e..b99aa10b26c 100644 --- a/packages/wasm-dpp/Cargo.toml +++ b/packages/wasm-dpp/Cargo.toml @@ -26,3 +26,5 @@ anyhow = "1.0.66" [profile.release] lto = true opt-level = 's' +debug=true + diff --git a/packages/wasm-dpp/src/conversion.rs b/packages/wasm-dpp/src/conversion.rs new file mode 100644 index 00000000000..efb7d6417d5 --- /dev/null +++ b/packages/wasm-dpp/src/conversion.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ConversionOptions { + #[serde(default)] + pub skip_identifiers_conversion: bool, +} diff --git a/packages/wasm-dpp/src/data_contract/state_transition/data_contract_create_transition/validation.rs b/packages/wasm-dpp/src/data_contract/state_transition/data_contract_create_transition/validation.rs index 74c754d1aa5..cafee55de3f 100644 --- a/packages/wasm-dpp/src/data_contract/state_transition/data_contract_create_transition/validation.rs +++ b/packages/wasm-dpp/src/data_contract/state_transition/data_contract_create_transition/validation.rs @@ -14,11 +14,11 @@ pub async fn validate_data_contract_create_transition_state( state_transition: DataContractCreateTransitionWasm, ) -> Result { let wrapped_state_repository = ExternalStateRepositoryLikeWrapper::new(state_repository); - dpp_validate_data_contract_create_transition_state( + let validation_result = dpp_validate_data_contract_create_transition_state( &wrapped_state_repository, &state_transition.into(), ) .await - .map(Into::::into) - .map_err(from_dpp_err) + .map_err(from_dpp_err)?; + Ok(validation_result.map(|_| JsValue::undefined()).into()) } diff --git a/packages/wasm-dpp/src/data_contract/state_transition/data_contract_update_transition/validation.rs b/packages/wasm-dpp/src/data_contract/state_transition/data_contract_update_transition/validation.rs index c6664209f79..d52fe21c4f0 100644 --- a/packages/wasm-dpp/src/data_contract/state_transition/data_contract_update_transition/validation.rs +++ b/packages/wasm-dpp/src/data_contract/state_transition/data_contract_update_transition/validation.rs @@ -19,13 +19,13 @@ pub async fn validate_data_contract_update_transition_state( state_transition: DataContractUpdateTransitionWasm, ) -> Result { let wrapped_state_repository = ExternalStateRepositoryLikeWrapper::new(state_repository); - dpp_validate_data_contract_update_transition_state( + let validation_result = dpp_validate_data_contract_update_transition_state( &wrapped_state_repository, &state_transition.into(), ) .await - .map(Into::into) - .map_err(from_dpp_err) + .map_err(from_dpp_err)?; + Ok(validation_result.map(|_| JsValue::undefined()).into()) } #[wasm_bindgen(js_name=validateIndicesAreBackwardCompatible)] @@ -40,7 +40,8 @@ pub fn validate_indices_are_backward_compatible( new_documents_schema, )?; - dpp_validate_indices_are_backward_compatible(old_documents.iter(), new_documents.iter()) - .map(Into::into) - .map_err(from_protocol_error) + let validation_result = + dpp_validate_indices_are_backward_compatible(old_documents.iter(), new_documents.iter()) + .map_err(from_protocol_error)?; + Ok(validation_result.map(|_| JsValue::undefined()).into()) } 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 00d3ed8ff03..75275d5ec11 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 @@ -47,10 +47,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"); - self.0 - .validate(&json_object) - .map(Into::into) - .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..0c58e3ac263 --- /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 + 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 db4f59c6ea3..1ee1fdbfafa 100644 --- a/packages/wasm-dpp/src/document/mod.rs +++ b/packages/wasm-dpp/src/document/mod.rs @@ -4,12 +4,13 @@ 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}; use crate::buffer::Buffer; +use crate::conversion::ConversionOptions; use crate::errors::RustConversionError; use crate::identifier::IdentifierWrapper; use crate::lodash::lodash_set; @@ -21,6 +22,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; @@ -34,12 +36,6 @@ pub(super) enum BinaryType { 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); @@ -154,9 +150,33 @@ 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())?; + let (identifier_paths, binary_paths) = self + .0 + .data_contract + .get_identifiers_and_binary_paths(&self.0.document_type); + + 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(with_js_error!(self.0.get_data().serialize(&serializer))?) + 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 d25541845e0..750e07dc5a8 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,38 +37,206 @@ 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); + // 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.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); + + 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); + + 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); - 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()); } @@ -65,3 +245,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 6b2879c0de2..c22052ab2b2 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,37 +38,102 @@ 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); + // 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); + + 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); - 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()); } @@ -61,4 +141,116 @@ 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); + + 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 41c729666aa..e14857004f8 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,18 @@ 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, + conversion::ConversionOptions, + identifier::IdentifierWrapper, + lodash::lodash_set, + utils::{ToSerdeJSONExt, WithJsError}, + with_js_error, BinaryType, DataContractWasm, }; #[wasm_bindgen] @@ -82,17 +86,15 @@ impl DocumentTransitionWasm { #[wasm_bindgen(js_name=getObject)] pub fn to_object(&self, options: &JsValue) -> Result { - // TODO options?? - 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 +144,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 24720e6547a..c7a0ac8a9e2 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 @@ -11,7 +11,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::{ @@ -38,6 +38,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)] @@ -153,15 +162,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)?; @@ -169,7 +178,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 @@ -188,12 +197,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/identity_facade.rs b/packages/wasm-dpp/src/identity/identity_facade.rs index 63e0c5cc87f..878c2a663a3 100644 --- a/packages/wasm-dpp/src/identity/identity_facade.rs +++ b/packages/wasm-dpp/src/identity/identity_facade.rs @@ -38,10 +38,8 @@ impl IdentityFacadeWasm { let identity_json = serde_wasm_bindgen::from_value(raw_identity_object) .expect("unable to serialize identity"); // TODO: handle the case when - self.0 - .validate(&identity_json) - .map(|res| res.into()) - .map_err(|err| err.into()) + let validation_result = self.0.validate(&identity_json)?; + Ok(validation_result.map(|_| JsValue::undefined()).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 6f1b63e9c47..01db1a90eac 100644 --- a/packages/wasm-dpp/src/identity/validation/public_keys_validator.rs +++ b/packages/wasm-dpp/src/identity/validation/public_keys_validator.rs @@ -36,10 +36,11 @@ impl PublicKeysValidatorWasm { ) -> Result { let raw_public_keys = to_vec_of_serde_values(public_keys.iter())?; - self.public_key_validator + let validation_result = self + .public_key_validator .validate_keys(&raw_public_keys) - .map(ValidationResultWasm::from) - .map_err(|e| JsValue::from(e.to_string())) + .map_err(|e| JsValue::from(e.to_string()))?; + Ok(validation_result.map(|_| JsValue::undefined()).into()) } #[wasm_bindgen(js_name=validatePublicKeyStructure)] @@ -49,10 +50,11 @@ impl PublicKeysValidatorWasm { ) -> Result { let pk_serde_json = public_key.with_serde_to_json_value()?; - self.public_key_validator + let validation_result = self + .public_key_validator .validate_public_key_structure(&pk_serde_json) - .map(ValidationResultWasm::from) - .map_err(|e| JsValue::from(e.to_string())) + .map_err(|e| JsValue::from(e.to_string()))?; + Ok(validation_result.map(|_| JsValue::undefined()).into()) } #[wasm_bindgen(js_name=validateKeysInStateTransition)] @@ -62,9 +64,10 @@ impl PublicKeysValidatorWasm { ) -> Result { let raw_public_keys = to_vec_of_serde_values(public_keys.iter())?; - self.public_key_in_state_transition_validator + let validation_result = self + .public_key_in_state_transition_validator .validate_keys(&raw_public_keys) - .map(ValidationResultWasm::from) - .map_err(|e| JsValue::from(e.to_string())) + .map_err(|e| JsValue::from(e.to_string()))?; + Ok(validation_result.map(|_| JsValue::undefined()).into()) } } diff --git a/packages/wasm-dpp/src/lib.rs b/packages/wasm-dpp/src/lib.rs index a0e744b0cf0..512417d337e 100644 --- a/packages/wasm-dpp/src/lib.rs +++ b/packages/wasm-dpp/src/lib.rs @@ -22,5 +22,6 @@ mod utils; mod bls_adapter; mod buffer; +mod conversion; mod lodash; mod validation; diff --git a/packages/wasm-dpp/src/utils.rs b/packages/wasm-dpp/src/utils.rs index c4bd9ae5f12..49f4e745fcf 100644 --- a/packages/wasm-dpp/src/utils.rs +++ b/packages/wasm-dpp/src/utils.rs @@ -3,10 +3,10 @@ use dpp::{ ProtocolError, }; -use js_sys::Function; +use js_sys::{Function, Object, Reflect}; use serde::de::DeserializeOwned; use serde_json::Value; -use wasm_bindgen::prelude::*; +use wasm_bindgen::{convert::RefFromWasmAbi, prelude::*}; use crate::errors::{from_dpp_err, RustConversionError}; diff --git a/packages/wasm-dpp/src/validation/validation_result.rs b/packages/wasm-dpp/src/validation/validation_result.rs index fa096239420..47d8c412fed 100644 --- a/packages/wasm-dpp/src/validation/validation_result.rs +++ b/packages/wasm-dpp/src/validation/validation_result.rs @@ -4,7 +4,16 @@ use js_sys::JsString; use wasm_bindgen::prelude::*; #[wasm_bindgen(js_name=ValidationResult)] -pub struct ValidationResultWasm(ValidationResult<()>); +pub struct ValidationResultWasm(ValidationResult); + +impl From> for ValidationResultWasm +where + T: Into + Clone, +{ + fn from(validation_result: ValidationResult) -> Self { + ValidationResultWasm(validation_result.map(Into::into)) + } +} #[wasm_bindgen(js_class=ValidationResult)] impl ValidationResultWasm { @@ -32,10 +41,9 @@ impl ValidationResultWasm { .map(from_consensus_error_ref) .collect() } -} -impl From> for ValidationResultWasm { - fn from(validation_result: ValidationResult<()>) -> Self { - ValidationResultWasm(validation_result) + #[wasm_bindgen(js_name=getData)] + pub fn get_data(&self) -> JsValue { + self.0.data().unwrap_or(&JsValue::undefined()).to_owned() } } diff --git a/packages/wasm-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js b/packages/wasm-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js index 1b36479fad9..0f793edb2ab 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.fetchDataContract.resolves(dataContract); + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.fetchDataContract.returns(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.returns(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, }); }); }); From 471dfaef7d141d29873b0d16a1813195f6556ffc Mon Sep 17 00:00:00 2001 From: Pawel Iwan Date: Fri, 27 Jan 2023 07:06:02 +0100 Subject: [PATCH 2/7] fixes --- .../validation/state/validatePublicKeysState.js | 4 +++- packages/wasm-dpp/Cargo.toml | 2 -- .../src/data_contract_factory/data_contract_factory.rs | 1 + .../wasm-dpp/src/document/fetch_and_validate_data_contract.rs | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.js b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.js index ea75fd4677a..3416355f2a9 100644 --- a/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.js +++ b/packages/js-dpp/lib/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.js @@ -21,8 +21,10 @@ const MaxIdentityPublicKeyLimitReachedError = require( * @param {RawIdentityPublicKey[]} rawPublicKeys * * @return {ValidationResult} - */ function validatePublicKeysState(rawPublicKeys) { + */ +function validatePublicKeysState(rawPublicKeys) { const result = new ValidationResult(); + if (rawPublicKeys.length > identitySchema.properties.publicKeys.maxItems) { result.addError( new MaxIdentityPublicKeyLimitReachedError(identitySchema.properties.publicKeys.maxItems), diff --git a/packages/wasm-dpp/Cargo.toml b/packages/wasm-dpp/Cargo.toml index b99aa10b26c..8780683152e 100644 --- a/packages/wasm-dpp/Cargo.toml +++ b/packages/wasm-dpp/Cargo.toml @@ -26,5 +26,3 @@ anyhow = "1.0.66" [profile.release] lto = true opt-level = 's' -debug=true - 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 837cf7ea6d6..2d9f886311f 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 @@ -47,6 +47,7 @@ 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)?; 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 index 0c58e3ac263..7e1be765675 100644 --- a/packages/wasm-dpp/src/document/fetch_and_validate_data_contract.rs +++ b/packages/wasm-dpp/src/document/fetch_and_validate_data_contract.rs @@ -27,7 +27,7 @@ pub async fn fetch_and_validate_data_contract_wasm( let _ = document_value.replace_identifier_paths(document::IDENTIFIER_FIELDS, ReplaceWith::Bytes); - // TODO! remove the context + // 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) From c0c6fc839b40fb9f02e9a1d266e7901da7685686 Mon Sep 17 00:00:00 2001 From: Pawel Iwan Date: Fri, 27 Jan 2023 07:15:38 +0100 Subject: [PATCH 3/7] fixes after merge --- packages/wasm-dpp/src/document/mod.rs | 3 ++- .../document_transition/document_create_transition.rs | 10 +++++++--- .../document_transition/document_replace_transition.rs | 10 +++++++--- packages/wasm-dpp/src/utils.rs | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/wasm-dpp/src/document/mod.rs b/packages/wasm-dpp/src/document/mod.rs index 6d1a804e9b3..c1f7d1f9804 100644 --- a/packages/wasm-dpp/src/document/mod.rs +++ b/packages/wasm-dpp/src/document/mod.rs @@ -158,7 +158,8 @@ impl DocumentWasm { let (identifier_paths, binary_paths) = self .0 .data_contract - .get_identifiers_and_binary_paths(&self.0.document_type); + .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) { 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 840e8d9d7a9..5a8231cd10a 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 @@ -48,7 +48,9 @@ impl DocumentCreateTransitionWasm { .get_string(document::property_names::DOCUMENT_TYPE) .with_js_error()?; - let (identifier_paths, _) = data_contract.get_identifiers_and_binary_paths(document_type); + 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 @@ -147,7 +149,8 @@ impl DocumentCreateTransitionWasm { .inner .base .data_contract - .get_identifiers_and_binary_paths(&self.inner.base.document_type); + .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) { @@ -187,7 +190,8 @@ impl DocumentCreateTransitionWasm { .inner .base .data_contract - .get_identifiers_and_binary_paths(&self.inner.base.document_type); + .get_identifiers_and_binary_paths(&self.inner.base.document_type) + .with_js_error()?; to_object( &self.inner, 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 e8950b4f620..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 @@ -51,7 +51,9 @@ impl DocumentReplaceTransitionWasm { .get_string(document::property_names::DOCUMENT_TYPE) .with_js_error()?; - let (identifier_paths, _) = data_contract.get_identifiers_and_binary_paths(document_type); + 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 @@ -86,7 +88,8 @@ impl DocumentReplaceTransitionWasm { .inner .base .data_contract - .get_identifiers_and_binary_paths(&self.inner.base.document_type); + .get_identifiers_and_binary_paths(&self.inner.base.document_type) + .with_js_error()?; to_object( &self.inner, @@ -202,7 +205,8 @@ impl DocumentReplaceTransitionWasm { .inner .base .data_contract - .get_identifiers_and_binary_paths(&self.inner.base.document_type); + .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) { diff --git a/packages/wasm-dpp/src/utils.rs b/packages/wasm-dpp/src/utils.rs index 432b2f8bf21..c4f2a85070c 100644 --- a/packages/wasm-dpp/src/utils.rs +++ b/packages/wasm-dpp/src/utils.rs @@ -3,7 +3,7 @@ use dpp::{ ProtocolError, }; -use js_sys::{Function, Object, Reflect}; +use js_sys::Function; use serde::de::DeserializeOwned; use serde_json::Value; use wasm_bindgen::{convert::RefFromWasmAbi, prelude::*}; From 180387f5dc29ffad7165d00d17be99837dfe2821 Mon Sep 17 00:00:00 2001 From: Pawel Iwan Date: Fri, 27 Jan 2023 07:21:56 +0100 Subject: [PATCH 4/7] removing conversion mod --- packages/wasm-dpp/src/conversion.rs | 8 -------- packages/wasm-dpp/src/document/mod.rs | 8 +++++++- .../document_batch_transition/document_transition/mod.rs | 3 +-- packages/wasm-dpp/src/lib.rs | 1 - 4 files changed, 8 insertions(+), 12 deletions(-) delete mode 100644 packages/wasm-dpp/src/conversion.rs diff --git a/packages/wasm-dpp/src/conversion.rs b/packages/wasm-dpp/src/conversion.rs deleted file mode 100644 index efb7d6417d5..00000000000 --- a/packages/wasm-dpp/src/conversion.rs +++ /dev/null @@ -1,8 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)] -#[serde(rename_all = "camelCase")] -pub(crate) struct ConversionOptions { - #[serde(default)] - pub skip_identifiers_conversion: bool, -} diff --git a/packages/wasm-dpp/src/document/mod.rs b/packages/wasm-dpp/src/document/mod.rs index c1f7d1f9804..3dc4d370a4b 100644 --- a/packages/wasm-dpp/src/document/mod.rs +++ b/packages/wasm-dpp/src/document/mod.rs @@ -10,7 +10,6 @@ use wasm_bindgen::prelude::*; use dpp::document::{property_names, Document, IDENTIFIER_FIELDS}; use crate::buffer::Buffer; -use crate::conversion::ConversionOptions; use crate::errors::RustConversionError; use crate::identifier::IdentifierWrapper; use crate::lodash::lodash_set; @@ -30,6 +29,13 @@ 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, 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 e14857004f8..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 @@ -16,11 +16,10 @@ use wasm_bindgen::prelude::*; use crate::{ buffer::Buffer, - conversion::ConversionOptions, identifier::IdentifierWrapper, lodash::lodash_set, utils::{ToSerdeJSONExt, WithJsError}, - with_js_error, BinaryType, DataContractWasm, + with_js_error, BinaryType, ConversionOptions, DataContractWasm, }; #[wasm_bindgen] diff --git a/packages/wasm-dpp/src/lib.rs b/packages/wasm-dpp/src/lib.rs index 512417d337e..a0e744b0cf0 100644 --- a/packages/wasm-dpp/src/lib.rs +++ b/packages/wasm-dpp/src/lib.rs @@ -22,6 +22,5 @@ mod utils; mod bls_adapter; mod buffer; -mod conversion; mod lodash; mod validation; From 8adeadd214c3562191b49ac39fff223a6a1c7cbc Mon Sep 17 00:00:00 2001 From: Pawel Iwan Date: Wed, 15 Feb 2023 10:11:03 +0100 Subject: [PATCH 5/7] test fixes --- packages/wasm-dpp/src/state_repository.rs | 2 +- .../unit/document/fetchAndValidateDataContractFactory.spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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/test/unit/document/fetchAndValidateDataContractFactory.spec.js b/packages/wasm-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js index 0f793edb2ab..76ed727c4dc 100644 --- a/packages/wasm-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js +++ b/packages/wasm-dpp/test/unit/document/fetchAndValidateDataContractFactory.spec.js @@ -43,7 +43,7 @@ describe('fetchAndValidateDataContractFactory', () => { stateRepositoryMockJs.fetchDataContract.resolves(dataContractJs); stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); - stateRepositoryMock.fetchDataContract.returns(dataContract); + stateRepositoryMock.fetchDataContract.resolves(dataContract); fetchAndValidateDataContractJs = fetchAndValidateDataContractFactory( stateRepositoryMockJs, @@ -85,7 +85,7 @@ describe('fetchAndValidateDataContractFactory', () => { }); it('should return with invalid result if Data Contract is not present - Rust', async () => { - stateRepositoryMock.fetchDataContract.returns(null); + stateRepositoryMock.fetchDataContract.resolves(null); const result = await fetchAndValidateDataContract(stateRepositoryMock, rawDocument); From 53884c4b97507a4adaa60e34803cc2f4414839d8 Mon Sep 17 00:00:00 2001 From: Pawel Iwan Date: Wed, 15 Feb 2023 10:13:55 +0100 Subject: [PATCH 6/7] remove comments --- packages/wasm-dpp/src/document/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/wasm-dpp/src/document/mod.rs b/packages/wasm-dpp/src/document/mod.rs index 6df1b3f40da..c32e857de39 100644 --- a/packages/wasm-dpp/src/document/mod.rs +++ b/packages/wasm-dpp/src/document/mod.rs @@ -185,7 +185,6 @@ impl DocumentWasm { } } - // Ok(with_js_error!(self.0.data.serialize(&serializer))?) Ok(js_value) } From ef3892ffb7cfa9c0a6cb2330e2c21a931963ff73 Mon Sep 17 00:00:00 2001 From: Pawel Iwan Date: Thu, 23 Feb 2023 07:15:02 +0100 Subject: [PATCH 7/7] address comments --- .../document_transition/document_create_transition.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 5a8231cd10a..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 @@ -108,10 +108,10 @@ impl DocumentCreateTransitionWasm { #[wasm_bindgen(js_name=getDataContractId)] pub fn data_contract_id(&self) -> IdentifierWrapper { - self.inner.base.data_contract.id.clone().into() + self.inner.base.data_contract.id.into() } - #[wasm_bindgen(js_name=get)] + #[wasm_bindgen] pub fn get(&self, path: String) -> Result { let document_data = if let Some(ref data) = self.inner.data { data @@ -134,7 +134,7 @@ impl DocumentCreateTransitionWasm { BinaryType::Identifier => { let bytes: Vec = serde_json::from_value(value).unwrap(); let id = >::from( - Identifier::from_bytes(&bytes).unwrap(), + Identifier::from_bytes(&bytes).with_js_error()?, ); return Ok(id.into()); }