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 9db913213f5..a5f90de3bb4 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 @@ -24,6 +24,7 @@ use std::collections::HashSet; use std::collections::{BTreeMap, BTreeSet}; use std::convert::TryInto; +use crate::consensus::basic::data_contract::ContestedUniqueIndexOnMutableDocumentTypeError; #[cfg(any(test, feature = "validation"))] use crate::consensus::basic::data_contract::InvalidDocumentTypeNameError; #[cfg(feature = "validation")] @@ -51,8 +52,6 @@ use crate::version::PlatformVersion; use crate::ProtocolError; use platform_value::btreemap_extensions::BTreeValueMapHelper; use platform_value::{Identifier, Value}; - -const UNIQUE_INDEX_LIMIT_V0: usize = 16; const NOT_ALLOWED_SYSTEM_PROPERTIES: [&str; 1] = ["$id"]; const SYSTEM_PROPERTIES: [&str; 11] = [ @@ -307,6 +306,9 @@ impl DocumentTypeV0 { #[cfg(feature = "validation")] let mut unique_indices_count = 0; + #[cfg(feature = "validation")] + let mut contested_indices_count = 0; + let indices: BTreeMap = index_values .map(|index_values| { index_values @@ -332,11 +334,56 @@ impl DocumentTypeV0 { // so we need to limit their number to prevent of spikes and DoS attacks if index.unique { unique_indices_count += 1; - if unique_indices_count > UNIQUE_INDEX_LIMIT_V0 { + if unique_indices_count + > platform_version + .dpp + .validation + .document_type + .unique_index_limit + { return Err(ProtocolError::ConsensusError(Box::new( UniqueIndicesLimitReachedError::new( name.to_string(), - UNIQUE_INDEX_LIMIT_V0, + platform_version + .dpp + .validation + .document_type + .unique_index_limit, + false, + ) + .into(), + ))); + } + } + + if index.contested_index.is_some() { + contested_indices_count += 1; + if contested_indices_count + > platform_version + .dpp + .validation + .document_type + .contested_index_limit + { + return Err(ProtocolError::ConsensusError(Box::new( + UniqueIndicesLimitReachedError::new( + name.to_string(), + platform_version + .dpp + .validation + .document_type + .contested_index_limit, + true, + ) + .into(), + ))); + } + + if documents_mutable { + return Err(ProtocolError::ConsensusError(Box::new( + ContestedUniqueIndexOnMutableDocumentTypeError::new( + name.to_string(), + index.name, ) .into(), ))); diff --git a/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs b/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs index 6569a38eaa0..68c782268c6 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs @@ -7,11 +7,12 @@ use crate::consensus::basic::data_contract::data_contract_max_depth_exceed_error #[cfg(feature = "json-schema-validation")] use crate::consensus::basic::data_contract::InvalidJsonSchemaRefError; use crate::consensus::basic::data_contract::{ - DataContractHaveNewUniqueIndexError, DataContractImmutablePropertiesUpdateError, - DataContractInvalidIndexDefinitionUpdateError, DataContractUniqueIndicesChangedError, - DuplicateIndexError, DuplicateIndexNameError, IncompatibleDataContractSchemaError, - IncompatibleDocumentTypeSchemaError, IncompatibleRe2PatternError, InvalidCompoundIndexError, - InvalidDataContractIdError, InvalidDataContractVersionError, InvalidDocumentTypeNameError, + ContestedUniqueIndexOnMutableDocumentTypeError, DataContractHaveNewUniqueIndexError, + DataContractImmutablePropertiesUpdateError, DataContractInvalidIndexDefinitionUpdateError, + DataContractUniqueIndicesChangedError, DuplicateIndexError, DuplicateIndexNameError, + IncompatibleDataContractSchemaError, IncompatibleDocumentTypeSchemaError, + IncompatibleRe2PatternError, InvalidCompoundIndexError, InvalidDataContractIdError, + InvalidDataContractVersionError, InvalidDocumentTypeNameError, InvalidDocumentTypeRequiredSecurityLevelError, InvalidIndexPropertyTypeError, InvalidIndexedPropertyConstraintError, SystemPropertyIndexAlreadyPresentError, UndefinedIndexPropertyError, UniqueIndicesLimitReachedError, @@ -376,6 +377,9 @@ pub enum BasicError { #[error(transparent)] IncompatibleDocumentTypeSchemaError(IncompatibleDocumentTypeSchemaError), + #[error(transparent)] + ContestedUniqueIndexOnMutableDocumentTypeError(ContestedUniqueIndexOnMutableDocumentTypeError), + #[error(transparent)] OverflowError(OverflowError), } diff --git a/packages/rs-dpp/src/errors/consensus/basic/data_contract/contested_unique_index_on_mutable_document_type_error.rs b/packages/rs-dpp/src/errors/consensus/basic/data_contract/contested_unique_index_on_mutable_document_type_error.rs new file mode 100644 index 00000000000..ecd22d8e245 --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/basic/data_contract/contested_unique_index_on_mutable_document_type_error.rs @@ -0,0 +1,48 @@ +use crate::consensus::basic::BasicError; +use crate::consensus::ConsensusError; +use crate::errors::ProtocolError; +use bincode::{Decode, Encode}; +use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; +use thiserror::Error; + +#[derive( + Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, +)] +#[error( + "Document type '{document_type}' has a contested unique index '{contested_unique_index_name}'" +)] +#[platform_serialize(unversioned)] +pub struct ContestedUniqueIndexOnMutableDocumentTypeError { + /* + + DO NOT CHANGE ORDER OF FIELDS WITHOUT INTRODUCING OF NEW VERSION + + */ + document_type: String, + contested_unique_index_name: String, +} + +impl ContestedUniqueIndexOnMutableDocumentTypeError { + pub fn new(document_type: String, contested_unique_index_name: String) -> Self { + Self { + document_type, + contested_unique_index_name, + } + } + + pub fn document_type(&self) -> &str { + &self.document_type + } + + pub fn contested_unique_index_name(&self) -> &str { + &self.contested_unique_index_name + } +} + +impl From for ConsensusError { + fn from(err: ContestedUniqueIndexOnMutableDocumentTypeError) -> Self { + Self::BasicError(BasicError::ContestedUniqueIndexOnMutableDocumentTypeError( + err, + )) + } +} diff --git a/packages/rs-dpp/src/errors/consensus/basic/data_contract/mod.rs b/packages/rs-dpp/src/errors/consensus/basic/data_contract/mod.rs index 51526dceedd..1bd974b0998 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/data_contract/mod.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/data_contract/mod.rs @@ -1,3 +1,4 @@ +mod contested_unique_index_on_mutable_document_type_error; mod data_contract_have_new_unique_index_error; mod data_contract_immutable_properties_update_error; mod data_contract_invalid_index_definition_update_error; @@ -49,6 +50,7 @@ pub use system_property_index_already_present_error::*; pub use undefined_index_property_error::*; pub use unique_indices_limit_reached_error::*; +pub use contested_unique_index_on_mutable_document_type_error::*; pub use incompatible_document_type_schema_error::*; pub use invalid_document_type_name_error::*; pub use unknown_document_creation_restriction_mode_error::*; diff --git a/packages/rs-dpp/src/errors/consensus/basic/data_contract/unique_indices_limit_reached_error.rs b/packages/rs-dpp/src/errors/consensus/basic/data_contract/unique_indices_limit_reached_error.rs index 9e538deaf4c..e136354d260 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/data_contract/unique_indices_limit_reached_error.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/data_contract/unique_indices_limit_reached_error.rs @@ -8,7 +8,7 @@ use thiserror::Error; #[derive( Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, )] -#[error("'{document_type}' document has more than '{index_limit}' unique indexes")] +#[error("'{document_type}' document has more than '{index_limit}' unique indexes (contested is {is_contested_limit})")] #[platform_serialize(unversioned)] pub struct UniqueIndicesLimitReachedError { /* @@ -17,23 +17,29 @@ pub struct UniqueIndicesLimitReachedError { */ document_type: String, - index_limit: usize, + index_limit: u16, + is_contested_limit: bool, } impl UniqueIndicesLimitReachedError { - pub fn new(document_type: String, index_limit: usize) -> Self { + pub fn new(document_type: String, index_limit: u16, is_contested_limit: bool) -> Self { Self { document_type, index_limit, + is_contested_limit, } } pub fn document_type(&self) -> &str { &self.document_type } - pub fn index_limit(&self) -> usize { + pub fn index_limit(&self) -> u16 { self.index_limit } + + pub fn is_contested_limit(&self) -> bool { + self.is_contested_limit + } } impl From for ConsensusError { diff --git a/packages/rs-platform-version/src/version/dpp_versions.rs b/packages/rs-platform-version/src/version/dpp_versions.rs index a1fa69bb9d0..2b1d974b37d 100644 --- a/packages/rs-platform-version/src/version/dpp_versions.rs +++ b/packages/rs-platform-version/src/version/dpp_versions.rs @@ -88,6 +88,8 @@ pub struct VotingValidationVersions { #[derive(Clone, Debug, Default)] pub struct DocumentTypeValidationVersions { pub validate_update: FeatureVersion, + pub unique_index_limit: u16, + pub contested_index_limit: u16, } #[derive(Clone, Debug, Default)]