Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ where
state_repository: SR,
}

pub fn fetch_documents_factory<SR>(state_repository: SR) -> ApplyDataContractUpdateTransition<SR>
where
SR: StateRepositoryLike,
{
ApplyDataContractUpdateTransition { state_repository }
impl<SR: StateRepositoryLike> ApplyDataContractUpdateTransition<SR> {
pub fn new(state_repository: SR) -> Self {
ApplyDataContractUpdateTransition { state_repository }
}
}

impl<SR> ApplyDataContractUpdateTransition<SR>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,36 @@ pub fn validate_indices_are_backward_compatible<'a>(
})?
.get_indices()?,
);

let old_properties_set: HashSet<&str> = existing_schema
.get_schema_properties()?
.as_object()
.ok_or_else(|| {
anyhow!(
"the document '{}' properties in old schema must be an object",
document_type
)
})?
.keys()
.map(|x| x.as_ref())
.collect();
let new_properties_set: HashSet<&str> = new_documents_by_type
.get(document_type)
.expect("checked above")
.get_schema_properties()?
.as_object()
.ok_or_else(|| {
anyhow!(
"the document '{}' properties in new schema must be an object",
document_type
)
})?
.keys()
.map(|x| x.as_ref())
.collect();

let added_properties = new_properties_set.difference(&old_properties_set);

let existing_schema_indices = existing_schema.get_indices().unwrap_or_default();

let maybe_changed_unique_existing_index =
Expand Down Expand Up @@ -69,10 +99,10 @@ pub fn validate_indices_are_backward_compatible<'a>(
index_name: index.name.clone(),
})
}

let maybe_wrongly_constructed_new_index = get_wrongly_constructed_new_index(
existing_schema_indices.iter(),
name_new_index_map.values(),
added_properties.map(|x| *x),
)?;
if let Some(index) = maybe_wrongly_constructed_new_index {
result.add_error(BasicError::DataContractInvalidIndexDefinitionUpdateError {
Expand Down Expand Up @@ -113,10 +143,12 @@ fn indexes_are_not_equal(index_a: &Index, index_b: Option<&Index>) -> bool {
fn get_wrongly_constructed_new_index<'a>(
existing_schema_indices: impl IntoIterator<Item = &'a Index>,
new_schema_indices: impl IntoIterator<Item = &'a Index>,
added_properties: impl IntoIterator<Item = &'a str>,
) -> Result<Option<&'a Index>, ProtocolError> {
let mut existing_index_names: HashSet<&String> = Default::default();
let mut existing_indexed_properties: HashSet<&String> = Default::default();
let mut possible_sequences_of_properties: HashSet<&[IndexProperty]> = Default::default();
let added_properties_set: HashSet<&str> = added_properties.into_iter().collect();

for existing_index in existing_schema_indices {
existing_index_names.insert(&existing_index.name);
Expand All @@ -130,16 +162,26 @@ fn get_wrongly_constructed_new_index<'a>(
.filter(|index| !existing_index_names.contains(&&index.name));

for new_index in new_indices {
let existing_properties_len = new_index
let existing_indexed_properties_len = new_index
.properties
.iter()
.filter(|prop| existing_indexed_properties.contains(&&prop.name))
.count();
if existing_properties_len == 0 {
continue;

if existing_indexed_properties_len == 0 {
// Creating a new index for unindexed field is not ok unless it's a new field:
if let Some(property) = new_index.properties.first() {
if new_index.properties.len() == 1 && added_properties_set.contains(&*property.name)
{
continue;
}
} else {
return Ok(Some(new_index));
}
}

let properties_sequence = &new_index.properties[..existing_properties_len];
let properties_sequence = &new_index.properties[..existing_indexed_properties_len];

if !possible_sequences_of_properties.contains(properties_sequence) {
return Ok(Some(new_index));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,11 @@ pub async fn validate_data_contract_update_transition_state(
// Version difference should be exactly 1
let old_version = existing_data_contract.version;
let new_version = state_transition.data_contract.version;
let version_diff = new_version - old_version;

if version_diff != 1 {
if new_version < old_version || new_version - old_version != 1 {
let err = BasicError::InvalidDataContractVersionError {
expected_version: old_version + 1,
version: old_version + version_diff,
version: new_version,
};
result.add_error(err);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/rs-dpp/src/errors/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ impl ErrorWithCode for BasicError {

// Data contract
Self::JsonSchemaCompilationError { .. } => 1004,
Self::InvalidDataContractVersionError { .. } => 4013,
Self::InvalidDataContractVersionError { .. } => 1050,
Self::DataContractMaxDepthExceedError { .. } => 1007,
Self::DuplicateIndexNameError { .. } => 1048,
Self::InvalidJsonSchemaRefError { .. } => 1014,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,53 @@ fn should_return_valid_result_if_indices_are_not_changed() {

assert!(result.is_valid());
}

#[test]
fn should_return_invalid_result_if_non_unique_index_added_for_non_indexed_property() {
let TestData {
mut old_documents_schema,
mut new_documents_schema,
..
} = setup_test();

// Here we add another property to old schema that certainly was not indexed by any means.
old_documents_schema.get_mut("indexedDocument").unwrap()["properties"]
["oldUnindexedProperty"] = json!({
"type": "string",
"maxLength": "420",
});

new_documents_schema.get_mut("indexedDocument").unwrap()["indices"]
.push(json!(
{
"name": "index1337",
"properties": [
{
"oldUnindexedProperty": "asc",
},
],
"unique": false,
}
))
.unwrap();
let result = validate_indices_are_backward_compatible(
old_documents_schema.iter(),
new_documents_schema.iter(),
)
.expect("validation result should be returned");

assert_eq!(result.errors().len(), 1);
// TODO the error doesn't have assigned error code
assert_eq!(result.errors()[0].code(), 0);

let basic_error = get_basic_error(&result, 0);
assert!(matches!(
basic_error,
BasicError::DataContractInvalidIndexDefinitionUpdateError {
document_type,
index_name,
} if {
document_type == "indexedDocument" &&
index_name == "index1337"
}));
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ impl From<DataContractCreateTransition> for DataContractCreateTransitionWasm {
}
}

impl Into<DataContractCreateTransition> for DataContractCreateTransitionWasm {
fn into(self) -> DataContractCreateTransition {
self.0
impl From<DataContractCreateTransitionWasm> for DataContractCreateTransition {
fn from(val: DataContractCreateTransitionWasm) -> Self {
val.0
}
}

Expand Down Expand Up @@ -69,7 +69,7 @@ impl DataContractCreateTransitionWasm {

#[wasm_bindgen(js_name=getProtocolVersion)]
pub fn get_protocol_version(&self) -> u32 {
self.0.protocol_version.into()
self.0.protocol_version
}

#[wasm_bindgen(js_name=getEntropy)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use std::ops::Deref;

use dpp::data_contract::state_transition::apply_data_contract_update_transition_factory::ApplyDataContractUpdateTransition;
use wasm_bindgen::prelude::*;

use crate::{
state_repository::{ExternalStateRepositoryLike, ExternalStateRepositoryLikeWrapper},
DataContractUpdateTransitionWasm,
};

#[wasm_bindgen(js_name=ApplyDataContractUpdateTransition)]
pub struct ApplyDataContractUpdateTransitionWasm(
ApplyDataContractUpdateTransition<ExternalStateRepositoryLikeWrapper>,
);

impl From<ApplyDataContractUpdateTransition<ExternalStateRepositoryLikeWrapper>>
for ApplyDataContractUpdateTransitionWasm
{
fn from(wa: ApplyDataContractUpdateTransition<ExternalStateRepositoryLikeWrapper>) -> Self {
ApplyDataContractUpdateTransitionWasm(wa)
}
}

#[wasm_bindgen(js_class=ApplyDataContractUpdateTransition)]
impl ApplyDataContractUpdateTransitionWasm {
#[wasm_bindgen(constructor)]
pub fn new(
state_repository: ExternalStateRepositoryLike,
) -> ApplyDataContractUpdateTransitionWasm {
let pinned_js_state_repository = ExternalStateRepositoryLikeWrapper::new(state_repository);
ApplyDataContractUpdateTransition::new(pinned_js_state_repository).into()
}

#[wasm_bindgen(js_name=applyDataContractUpdateTransition)]
pub async fn apply_data_contract_update_transition(
&self,
transition: DataContractUpdateTransitionWasm,
) -> Result<(), JsError> {
self.0
.apply_data_contract_update_transition(&transition.into())
.await
.map_err(|e| e.deref().into())
}
}
Loading