diff --git a/packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v0/mod.rs index 97534fe049e..9c79f5c2812 100644 --- a/packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v0/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v0/mod.rs @@ -39,7 +39,9 @@ use crate::data_contract::document_type::property_names::{ CAN_BE_DELETED, CREATION_RESTRICTION_MODE, DOCUMENTS_KEEP_HISTORY, DOCUMENTS_MUTABLE, TRADE_MODE, TRANSFERABLE, }; -use crate::data_contract::document_type::{property_names, DocumentType}; +use crate::data_contract::document_type::{ + property_names, ByteArrayPropertySizes, DocumentType, StringPropertySizes, +}; use crate::data_contract::errors::DataContractError; use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; use crate::identity::SecurityLevel; @@ -383,7 +385,7 @@ impl DocumentTypeV0 { })?; // Validate indexed property type - match property_definition.property_type { + match &property_definition.property_type { // Array and objects aren't supported for indexing yet DocumentPropertyType::Array(_) | DocumentPropertyType::Object(_) @@ -399,9 +401,9 @@ impl DocumentTypeV0 { ))) } // Indexed byte array size must be limited - DocumentPropertyType::ByteArray(_, maybe_max_size) - if maybe_max_size.is_none() - || maybe_max_size.unwrap() + DocumentPropertyType::ByteArray(sizes) + if sizes.max_size.is_none() + || sizes.max_size.unwrap() > MAX_INDEXED_BYTE_ARRAY_PROPERTY_LENGTH => { Err(ProtocolError::ConsensusError(Box::new( @@ -419,9 +421,9 @@ impl DocumentTypeV0 { ))) } // Indexed string length must be limited - DocumentPropertyType::String(_, maybe_max_length) - if maybe_max_length.is_none() - || maybe_max_length.unwrap() + DocumentPropertyType::String(sizes) + if sizes.max_length.is_none() + || sizes.max_length.unwrap() > MAX_INDEXED_STRING_PROPERTY_LENGTH => { Err(ProtocolError::ConsensusError(Box::new( @@ -567,12 +569,14 @@ fn insert_values( Some("application/x.dash.dpp.identifier") => { DocumentPropertyType::Identifier } - Some(_) | None => DocumentPropertyType::ByteArray( - inner_properties - .get_optional_integer(property_names::MIN_ITEMS)?, - inner_properties - .get_optional_integer(property_names::MAX_ITEMS)?, - ), + Some(_) | None => { + DocumentPropertyType::ByteArray(ByteArrayPropertySizes { + min_size: inner_properties + .get_optional_integer(property_names::MIN_ITEMS)?, + max_size: inner_properties + .get_optional_integer(property_names::MAX_ITEMS)?, + }) + } } } else { return Err(DataContractError::InvalidContractStructure( @@ -622,10 +626,12 @@ fn insert_values( } "string" => { - field_type = DocumentPropertyType::String( - inner_properties.get_optional_integer(property_names::MIN_LENGTH)?, - inner_properties.get_optional_integer(property_names::MAX_LENGTH)?, - ); + field_type = DocumentPropertyType::String(StringPropertySizes { + min_length: inner_properties + .get_optional_integer(property_names::MIN_LENGTH)?, + max_length: inner_properties + .get_optional_integer(property_names::MAX_LENGTH)?, + }); document_properties.insert( prefixed_property_key, DocumentProperty { @@ -673,10 +679,10 @@ fn insert_values_nested( let field_type = match type_value { "integer" => DocumentPropertyType::Integer, "number" => DocumentPropertyType::Number, - "string" => DocumentPropertyType::String( - inner_properties.get_optional_integer(property_names::MIN_LENGTH)?, - inner_properties.get_optional_integer(property_names::MAX_LENGTH)?, - ), + "string" => DocumentPropertyType::String(StringPropertySizes { + min_length: inner_properties.get_optional_integer(property_names::MIN_LENGTH)?, + max_length: inner_properties.get_optional_integer(property_names::MAX_LENGTH)?, + }), "array" => { // Only handling bytearrays for v1 // Return an error if it is not a byte array @@ -689,10 +695,14 @@ fn insert_values_nested( Some("application/x.dash.dpp.identifier") => { DocumentPropertyType::Identifier } - Some(_) | None => DocumentPropertyType::ByteArray( - inner_properties.get_optional_integer(property_names::MIN_ITEMS)?, - inner_properties.get_optional_integer(property_names::MAX_ITEMS)?, - ), + Some(_) | None => { + DocumentPropertyType::ByteArray(ByteArrayPropertySizes { + min_size: inner_properties + .get_optional_integer(property_names::MIN_ITEMS)?, + max_size: inner_properties + .get_optional_integer(property_names::MAX_ITEMS)?, + }) + } } } else { return Err(DataContractError::InvalidContractStructure( diff --git a/packages/rs-dpp/src/data_contract/document_type/property/mod.rs b/packages/rs-dpp/src/data_contract/document_type/property/mod.rs index e3a4003239b..b4d69b8e071 100644 --- a/packages/rs-dpp/src/data_contract/document_type/property/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/property/mod.rs @@ -28,14 +28,26 @@ pub struct DocumentProperty { pub required: bool, } +#[derive(Debug, PartialEq, Clone, Serialize)] +pub struct StringPropertySizes { + pub min_length: Option, + pub max_length: Option, +} + +#[derive(Debug, PartialEq, Clone, Serialize)] +pub struct ByteArrayPropertySizes { + pub min_size: Option, + pub max_size: Option, +} + // @append_only #[derive(Debug, PartialEq, Clone, Serialize)] pub enum DocumentPropertyType { ///Todo decompose integer Integer, Number, - String(Option, Option), // TODO use structure - ByteArray(Option, Option), // TODO user structure + String(StringPropertySizes), + ByteArray(ByteArrayPropertySizes), Identifier, Boolean, Date, @@ -62,8 +74,8 @@ impl DocumentPropertyType { match self { DocumentPropertyType::Integer => "integer".to_string(), DocumentPropertyType::Number => "number".to_string(), - DocumentPropertyType::String(_, _) => "string".to_string(), - DocumentPropertyType::ByteArray(_, _) => "byteArray".to_string(), + DocumentPropertyType::String(_) => "string".to_string(), + DocumentPropertyType::ByteArray(_) => "byteArray".to_string(), DocumentPropertyType::Identifier => "identifier".to_string(), DocumentPropertyType::Boolean => "boolean".to_string(), DocumentPropertyType::Date => "date".to_string(), @@ -77,13 +89,13 @@ impl DocumentPropertyType { match self { DocumentPropertyType::Integer => Some(8), DocumentPropertyType::Number => Some(8), - DocumentPropertyType::String(min_length, _) => match min_length { + DocumentPropertyType::String(sizes) => match sizes.min_length { None => Some(0), - Some(size) => Some(*size), + Some(size) => Some(size), }, - DocumentPropertyType::ByteArray(min_size, _) => match min_size { + DocumentPropertyType::ByteArray(sizes) => match sizes.min_size { None => Some(0), - Some(size) => Some(*size), + Some(size) => Some(size), }, DocumentPropertyType::Boolean => Some(1), DocumentPropertyType::Date => Some(8), @@ -101,13 +113,13 @@ impl DocumentPropertyType { match self { DocumentPropertyType::Integer => Some(8), DocumentPropertyType::Number => Some(8), - DocumentPropertyType::String(min_length, _) => match min_length { + DocumentPropertyType::String(sizes) => match sizes.min_length { None => Some(0), - Some(size) => Some(*size * 4), + Some(size) => Some(size * 4), }, - DocumentPropertyType::ByteArray(min_size, _) => match min_size { + DocumentPropertyType::ByteArray(sizes) => match sizes.min_size { None => Some(0), - Some(size) => Some(*size), + Some(size) => Some(size), }, DocumentPropertyType::Boolean => Some(1), DocumentPropertyType::Date => Some(8), @@ -125,13 +137,13 @@ impl DocumentPropertyType { match self { DocumentPropertyType::Integer => Some(8), DocumentPropertyType::Number => Some(8), - DocumentPropertyType::String(_, max_length) => match max_length { + DocumentPropertyType::String(sizes) => match sizes.max_length { None => Some(u16::MAX), - Some(size) => Some(*size * 4), + Some(size) => Some(size * 4), }, - DocumentPropertyType::ByteArray(_, max_size) => match max_size { + DocumentPropertyType::ByteArray(sizes) => match sizes.max_size { None => Some(u16::MAX), - Some(size) => Some(*size), + Some(size) => Some(size), }, DocumentPropertyType::Boolean => Some(1), DocumentPropertyType::Date => Some(8), @@ -149,13 +161,13 @@ impl DocumentPropertyType { match self { DocumentPropertyType::Integer => Some(8), DocumentPropertyType::Number => Some(8), - DocumentPropertyType::String(_, max_length) => match max_length { + DocumentPropertyType::String(sizes) => match sizes.max_length { None => Some(16383), - Some(size) => Some(*size), + Some(size) => Some(size), }, - DocumentPropertyType::ByteArray(_, max_size) => match max_size { + DocumentPropertyType::ByteArray(sizes) => match sizes.max_size { None => Some(u16::MAX), - Some(size) => Some(*size), + Some(size) => Some(size), }, DocumentPropertyType::Boolean => Some(1), DocumentPropertyType::Date => Some(8), @@ -231,7 +243,7 @@ impl DocumentPropertyType { match self { DocumentPropertyType::Integer => Value::I64(rng.gen::()), DocumentPropertyType::Number => Value::Float(rng.gen::()), - DocumentPropertyType::String(_, _) => { + DocumentPropertyType::String(_) => { let size = self.random_size(rng); Value::Text( rng.sample_iter(Alphanumeric) @@ -240,7 +252,7 @@ impl DocumentPropertyType { .collect(), ) } - DocumentPropertyType::ByteArray(_, _) => { + DocumentPropertyType::ByteArray(_) => { let size = self.random_size(rng); if self.min_size() == self.max_size() { match size { @@ -290,7 +302,7 @@ impl DocumentPropertyType { match self { DocumentPropertyType::Integer => Value::I64(rng.gen::()), DocumentPropertyType::Number => Value::Float(rng.gen::()), - DocumentPropertyType::String(_, _) => { + DocumentPropertyType::String(_) => { let size = self.min_size().unwrap(); Value::Text( rng.sample_iter(Alphanumeric) @@ -299,7 +311,7 @@ impl DocumentPropertyType { .collect(), ) } - DocumentPropertyType::ByteArray(_, _) => { + DocumentPropertyType::ByteArray(_) => { let size = self.min_size().unwrap(); Value::Bytes(rng.sample_iter(Standard).take(size as usize).collect()) } @@ -330,7 +342,7 @@ impl DocumentPropertyType { match self { DocumentPropertyType::Integer => Value::I64(rng.gen::()), DocumentPropertyType::Number => Value::Float(rng.gen::()), - DocumentPropertyType::String(_, _) => { + DocumentPropertyType::String(_) => { let size = self.max_size().unwrap(); Value::Text( rng.sample_iter(Alphanumeric) @@ -339,7 +351,7 @@ impl DocumentPropertyType { .collect(), ) } - DocumentPropertyType::ByteArray(_, _) => { + DocumentPropertyType::ByteArray(_) => { let size = self.max_size().unwrap(); Value::Bytes(rng.sample_iter(Standard).take(size as usize).collect()) } @@ -401,7 +413,7 @@ impl DocumentPropertyType { } } match self { - DocumentPropertyType::String(_, _) => { + DocumentPropertyType::String(_) => { let bytes = Self::read_varint_value(buf)?; let string = String::from_utf8(bytes).map_err(|_| { DataContractError::CorruptedSerialization( @@ -437,11 +449,11 @@ impl DocumentPropertyType { _ => Ok((Some(Value::Bool(true)), false)), } } - DocumentPropertyType::ByteArray(min, max) => { - match (min, max) { + DocumentPropertyType::ByteArray(sizes) => { + match (sizes.min_size, sizes.max_size) { (Some(min), Some(max)) if min == max => { // if min == max, then we don't need a varint for the length - let len = *min as usize; + let len = min as usize; let mut bytes = vec![0; len]; buf.read_exact(&mut bytes).map_err(|_| { DataContractError::DecodingContractError(DecodingError::new(format!( @@ -543,7 +555,7 @@ impl DocumentPropertyType { return Ok(vec![]); } match self { - DocumentPropertyType::String(_, _) => { + DocumentPropertyType::String(_) => { if let Value::Text(value) = value { let vec = value.into_bytes(); let mut r_vec = vec.len().encode_var_vec(); @@ -589,7 +601,7 @@ impl DocumentPropertyType { Ok(r_vec) } } - DocumentPropertyType::ByteArray(_, _) => { + DocumentPropertyType::ByteArray(_) => { let mut bytes = value.into_binary_bytes()?; let mut r_vec = bytes.len().encode_var_vec(); @@ -675,7 +687,7 @@ impl DocumentPropertyType { return Ok(vec![]); } return match self { - DocumentPropertyType::String(_, _) => { + DocumentPropertyType::String(_) => { let value_as_text = value.as_text().ok_or_else(get_field_type_matching_error)?; let vec = value_as_text.as_bytes().to_vec(); let mut r_vec = vec.len().encode_var_vec(); @@ -704,7 +716,7 @@ impl DocumentPropertyType { let value_as_f64 = value.to_float().map_err(ProtocolError::ValueError)?; Ok(value_as_f64.to_be_bytes().to_vec()) } - DocumentPropertyType::ByteArray(min, max) => match (min, max) { + DocumentPropertyType::ByteArray(sizes) => match (sizes.min_size, sizes.max_size) { (Some(min), Some(max)) if min == max => Ok(value.to_binary_bytes()?), _ => { let mut bytes = value.to_binary_bytes()?; @@ -786,7 +798,7 @@ impl DocumentPropertyType { return Ok(vec![]); } match self { - DocumentPropertyType::String(_, _) => { + DocumentPropertyType::String(_) => { let value_as_text = value.as_text().ok_or_else(get_field_type_matching_error)?; let vec = value_as_text.as_bytes().to_vec(); if vec.is_empty() { @@ -807,7 +819,7 @@ impl DocumentPropertyType { DocumentPropertyType::Number => Ok(Self::encode_float( value.to_float().map_err(ProtocolError::ValueError)?, )), - DocumentPropertyType::ByteArray(_, _) => { + DocumentPropertyType::ByteArray(_) => { value.to_binary_bytes().map_err(ProtocolError::ValueError) } DocumentPropertyType::Identifier => value @@ -839,16 +851,16 @@ impl DocumentPropertyType { // Given a field type and a value this function chooses and executes the right encoding method pub fn value_from_string(&self, str: &str) -> Result { match self { - DocumentPropertyType::String(min, max) => { - if let Some(min) = min { - if str.len() < *min as usize { + DocumentPropertyType::String(sizes) => { + if let Some(min) = sizes.min_length { + if str.len() < min as usize { return Err(DataContractError::FieldRequirementUnmet( "string is too small".to_string(), )); } } - if let Some(max) = max { - if str.len() > *max as usize { + if let Some(max) = sizes.max_length { + if str.len() > max as usize { return Err(DataContractError::FieldRequirementUnmet( "string is too big".to_string(), )); @@ -866,16 +878,16 @@ impl DocumentPropertyType { ) }) } - DocumentPropertyType::ByteArray(min, max) => { - if let Some(min) = min { - if str.len() / 2 < *min as usize { + DocumentPropertyType::ByteArray(sizes) => { + if let Some(min) = sizes.min_size { + if str.len() / 2 < min as usize { return Err(DataContractError::FieldRequirementUnmet( "byte array is too small".to_string(), )); } } - if let Some(max) = max { - if str.len() / 2 > *max as usize { + if let Some(max) = sizes.max_size { + if str.len() / 2 > max as usize { return Err(DataContractError::FieldRequirementUnmet( "byte array is too big".to_string(), )); diff --git a/packages/rs-dpp/src/data_contract/document_type/schema/find_identifier_and_binary_paths/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/schema/find_identifier_and_binary_paths/v0/mod.rs index 35fb76650d1..3fc2259f7ef 100644 --- a/packages/rs-dpp/src/data_contract/document_type/schema/find_identifier_and_binary_paths/v0/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/schema/find_identifier_and_binary_paths/v0/mod.rs @@ -32,7 +32,7 @@ impl DocumentTypeV0 { DocumentPropertyType::Identifier => { identifier_paths.insert(new_path); } - DocumentPropertyType::ByteArray(_, _) => { + DocumentPropertyType::ByteArray(_) => { binary_paths.insert(new_path); } DocumentPropertyType::Object(inner_properties) => { diff --git a/packages/rs-dpp/src/data_contract/document_type/v0/random_document_type.rs b/packages/rs-dpp/src/data_contract/document_type/v0/random_document_type.rs index 3e46c291ea1..35649093130 100644 --- a/packages/rs-dpp/src/data_contract/document_type/v0/random_document_type.rs +++ b/packages/rs-dpp/src/data_contract/document_type/v0/random_document_type.rs @@ -106,6 +106,7 @@ use crate::data_contract::document_type::v0::StatelessJsonSchemaLazyValidator; use crate::data_contract::document_type::{ v0::DocumentTypeV0, DocumentProperty, DocumentPropertyType, DocumentType, Index, }; +use crate::data_contract::document_type::{ByteArrayPropertySizes, StringPropertySizes}; use crate::document::transfer::Transferable; use crate::identity::SecurityLevel; use crate::nft::TradeMode; @@ -145,14 +146,17 @@ impl DocumentTypeV0 { let random_weight = rng.gen_range(0..total_weight); let document_type = if random_weight < field_weights.string_weight { let has_min_len = rng.gen_bool(parameters.field_bounds.string_has_min_len_chance); - let min_len = if has_min_len { + let min_length = if has_min_len { Some(rng.gen_range(parameters.field_bounds.string_min_len.clone())) } else { None }; // If a string property is used in an index it must have maxLength 63 or less (v1.0-dev) - let max_len = Some(63); - DocumentPropertyType::String(min_len, max_len) + let max_length = Some(63); + DocumentPropertyType::String(StringPropertySizes { + min_length, + max_length, + }) } else if random_weight < field_weights.string_weight + field_weights.integer_weight { DocumentPropertyType::Integer } else if random_weight @@ -179,14 +183,14 @@ impl DocumentTypeV0 { } else { let has_min_len = rng.gen_bool(parameters.field_bounds.byte_array_has_min_len_chance); - let min_len = if has_min_len { + let min_size = if has_min_len { Some(rng.gen_range(parameters.field_bounds.byte_array_min_len.clone())) } else { None }; // Indexed arrays must have maxItems 255 or less (v1.0-dev) - let max_len = Some(255); - DocumentPropertyType::ByteArray(min_len, max_len) + let max_size = Some(255); + DocumentPropertyType::ByteArray(ByteArrayPropertySizes { min_size, max_size }) }; DocumentProperty { @@ -263,14 +267,14 @@ impl DocumentTypeV0 { let mut position_counter = 0; let properties_json_schema = properties.iter().map(|(key, prop)| { let mut schema_part = match &prop.property_type { - DocumentPropertyType::String(min, max) => { + DocumentPropertyType::String(sizes) => { let mut schema = serde_json::Map::new(); schema.insert("type".to_string(), serde_json::Value::String("string".to_owned())); - if let Some(min_len) = min { - schema.insert("minLength".to_string(), serde_json::Value::Number(serde_json::Number::from(*min_len))); + if let Some(min_len) = sizes.min_length { + schema.insert("minLength".to_string(), serde_json::Value::Number(serde_json::Number::from(min_len))); } - if let Some(max_len) = max { - schema.insert("maxLength".to_string(), serde_json::Value::Number(serde_json::Number::from(*max_len))); + if let Some(max_len) = sizes.max_length { + schema.insert("maxLength".to_string(), serde_json::Value::Number(serde_json::Number::from(max_len))); } serde_json::Value::Object(schema) }, @@ -305,14 +309,14 @@ impl DocumentTypeV0 { DocumentPropertyType::Boolean => { serde_json::json!({"type": "boolean"}) }, - DocumentPropertyType::ByteArray(min, max) => { + DocumentPropertyType::ByteArray(sizes) => { let mut schema = serde_json::Map::new(); schema.insert("type".to_string(), serde_json::Value::String("array".to_owned())); - if let Some(min_len) = min { - schema.insert("minItems".to_string(), serde_json::Value::Number(serde_json::Number::from(*min_len))); + if let Some(min_len) = sizes.min_size { + schema.insert("minItems".to_string(), serde_json::Value::Number(serde_json::Number::from(min_len))); } - if let Some(max_len) = max { - schema.insert("maxItems".to_string(), serde_json::Value::Number(serde_json::Number::from(*max_len))); + if let Some(max_len) = sizes.max_size { + schema.insert("maxItems".to_string(), serde_json::Value::Number(serde_json::Number::from(max_len))); } schema.insert("byteArray".to_string(), serde_json::Value::Bool(true)); serde_json::Value::Object(schema) @@ -463,14 +467,17 @@ impl DocumentTypeV0 { let random_weight = rng.gen_range(0..total_weight); let document_type = if random_weight < field_weights.string_weight { let has_min_len = rng.gen_bool(parameters.field_bounds.string_has_min_len_chance); - let min_len = if has_min_len { + let min_length = if has_min_len { Some(rng.gen_range(parameters.field_bounds.string_min_len.clone())) } else { None }; // If a string property is used in an index it must have maxLength 63 or less (v1.0-dev) - let max_len = Some(63); - DocumentPropertyType::String(min_len, max_len) + let max_length = Some(63); + DocumentPropertyType::String(StringPropertySizes { + min_length, + max_length, + }) } else if random_weight < field_weights.string_weight + field_weights.integer_weight { DocumentPropertyType::Integer } else if random_weight @@ -497,14 +504,14 @@ impl DocumentTypeV0 { } else { let has_min_len = rng.gen_bool(parameters.field_bounds.byte_array_has_min_len_chance); - let min_len = if has_min_len { + let min_size = if has_min_len { Some(rng.gen_range(parameters.field_bounds.byte_array_min_len.clone())) } else { None }; // Indexed arrays must have maxItems 255 or less (v1.0-dev) - let max_len = Some(255); - DocumentPropertyType::ByteArray(min_len, max_len) + let max_size = Some(255); + DocumentPropertyType::ByteArray(ByteArrayPropertySizes { min_size, max_size }) }; DocumentProperty { diff --git a/packages/rs-dpp/src/data_contract/errors/contract.rs b/packages/rs-dpp/src/data_contract/errors/contract.rs index 5b152a0a6b1..32989597a00 100644 --- a/packages/rs-dpp/src/data_contract/errors/contract.rs +++ b/packages/rs-dpp/src/data_contract/errors/contract.rs @@ -3,7 +3,6 @@ use crate::consensus::basic::decode::DecodingError; use crate::consensus::basic::BasicError; use bincode::{Decode, Encode}; use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; -use platform_value::Identifier; use thiserror::Error; use crate::consensus::basic::document::InvalidDocumentTypeError; diff --git a/packages/rs-dpp/src/tests/fixtures/identity_fixture.rs b/packages/rs-dpp/src/tests/fixtures/identity_fixture.rs index b0e92a5850d..b8ff26652ab 100644 --- a/packages/rs-dpp/src/tests/fixtures/identity_fixture.rs +++ b/packages/rs-dpp/src/tests/fixtures/identity_fixture.rs @@ -1,10 +1,9 @@ use crate::identity::identity_public_key::v0::IdentityPublicKeyV0; -use crate::identity::{IdentityPublicKey, IdentityV0, KeyType, Purpose, SecurityLevel}; +use crate::identity::{IdentityV0, KeyType, Purpose, SecurityLevel}; use platform_value::platform_value; use platform_value::string_encoding::Encoding; use platform_value::BinaryData; use serde_json::json; -use std::collections::BTreeMap; use crate::prelude::{Identifier, Identity};