From 193702e73e4d92f6083126e8b3dcb514c6bb662d Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Fri, 14 Mar 2025 23:57:29 +0700 Subject: [PATCH 1/7] new document type --- .../evaluate_interval.rs | 1 - .../document_type/accessors/mod.rs | 2 + .../document_type/accessors/v1/mod.rs | 47 ++ .../class_methods/try_from_schema/v0/mod.rs | 2 +- .../contested_vote_poll_for_document/mod.rs | 1 - .../v0/mod.rs | 55 -- .../methods/create_document_from_data/mod.rs | 1 - .../create_document_from_data/v0/mod.rs | 182 ------ .../mod.rs | 1 - .../v0/mod.rs | 175 ------ .../methods/deserialize_value_for_key/mod.rs | 1 - .../deserialize_value_for_key/v0/mod.rs | 52 -- .../methods/estimated_size/mod.rs | 1 - .../methods/estimated_size/v0/mod.rs | 32 - .../methods/index_for_types/mod.rs | 1 - .../methods/index_for_types/v0/mod.rs | 28 - .../document_type/methods/max_size/mod.rs | 1 - .../document_type/methods/max_size/v0/mod.rs | 30 - .../document_type/methods/mod.rs | 255 +++----- .../mod.rs | 1 - .../v0/mod.rs | 42 -- .../methods/serialize_value_for_key/mod.rs | 1 - .../methods/serialize_value_for_key/v0/mod.rs | 63 -- .../methods/versioned_methods.rs | 591 ++++++++++++++++++ .../src/data_contract/document_type/mod.rs | 187 +----- .../document_type/random_document.rs | 473 +++++++------- .../document_type/token_costs/accessors.rs | 62 ++ .../document_type/token_costs/mod.rs | 111 ++++ .../document_type/token_costs/v0/mod.rs | 99 +++ .../src/data_contract/document_type/v0/mod.rs | 21 +- .../document_type/v0/random_document.rs | 310 --------- .../document_type/v1/accessors.rs | 138 ++++ .../src/data_contract/document_type/v1/mod.rs | 111 ++++ .../document_type/v1/random_document_type.rs | 40 ++ .../document_type/{v0 => }/validator.rs | 0 .../src/document/extended_document/v0/mod.rs | 4 +- packages/rs-dpp/src/document/v0/serialize.rs | 2 +- .../document_create_transition/v0/mod.rs | 2 +- .../document/batch_transition/methods/mod.rs | 1 + .../batch_transition/v1/v0_methods.rs | 6 +- .../basic-token-with-document.json | 158 +++++ .../contract/insert/insert_contract/v0/mod.rs | 2 +- .../contract/update/update_contract/v0/mod.rs | 2 +- .../add_document_to_primary_storage/v0/mod.rs | 2 +- .../v0/mod.rs | 2 +- .../v0/mod.rs | 2 +- .../v0/mod.rs | 2 +- packages/rs-drive/src/drive/document/paths.rs | 2 +- .../v0/mod.rs | 2 +- .../add_pre_programmed_distribution/v0/mod.rs | 1 - .../v0/mod.rs | 2 +- .../v0/mod.rs | 1 - .../v0/mod.rs | 11 +- .../v0/mod.rs | 2 +- .../v0/transformer.rs | 28 +- .../src/util/batch/drive_op_batch/token.rs | 1 - .../util/object_size_info/document_info.rs | 2 +- 57 files changed, 1736 insertions(+), 1619 deletions(-) create mode 100644 packages/rs-dpp/src/data_contract/document_type/accessors/v1/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/contested_vote_poll_for_document/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/contested_vote_poll_for_document/v0/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/create_document_from_data/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/create_document_from_data/v0/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/create_document_with_prevalidated_properties/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/create_document_with_prevalidated_properties/v0/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/deserialize_value_for_key/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/deserialize_value_for_key/v0/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/estimated_size/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/estimated_size/v0/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/index_for_types/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/index_for_types/v0/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/max_size/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/max_size/v0/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/prefunded_voting_balances_for_document/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/prefunded_voting_balances_for_document/v0/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/serialize_value_for_key/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/serialize_value_for_key/v0/mod.rs create mode 100644 packages/rs-dpp/src/data_contract/document_type/methods/versioned_methods.rs create mode 100644 packages/rs-dpp/src/data_contract/document_type/token_costs/accessors.rs create mode 100644 packages/rs-dpp/src/data_contract/document_type/token_costs/mod.rs create mode 100644 packages/rs-dpp/src/data_contract/document_type/token_costs/v0/mod.rs delete mode 100644 packages/rs-dpp/src/data_contract/document_type/v0/random_document.rs create mode 100644 packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs create mode 100644 packages/rs-dpp/src/data_contract/document_type/v1/mod.rs create mode 100644 packages/rs-dpp/src/data_contract/document_type/v1/random_document_type.rs rename packages/rs-dpp/src/data_contract/document_type/{v0 => }/validator.rs (100%) create mode 100644 packages/rs-drive-abci/tests/supporting_files/contract/basic-token-with-document/basic-token-with-document.json diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/reward_distribution_type/evaluate_interval.rs b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/reward_distribution_type/evaluate_interval.rs index e5b805b6655..373cbf28dea 100644 --- a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/reward_distribution_type/evaluate_interval.rs +++ b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/reward_distribution_type/evaluate_interval.rs @@ -1,5 +1,4 @@ use crate::balances::credits::TokenAmount; -use crate::block::block_info::BlockInfo; use crate::block::epoch::EpochIndex; use crate::data_contract::associated_token::token_perpetual_distribution::distribution_function::reward_ratio::RewardRatio; use crate::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment; diff --git a/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs b/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs index 377b852e25d..6fa3807633c 100644 --- a/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs @@ -1,4 +1,5 @@ mod v0; +mod v1; use crate::data_contract::document_type::index::Index; use crate::data_contract::document_type::index_level::IndexLevel; @@ -15,6 +16,7 @@ use crate::nft::TradeMode; use indexmap::IndexMap; use std::collections::{BTreeMap, BTreeSet}; pub use v0::*; +pub use v1::*; impl DocumentTypeV0Getters for DocumentType { fn name(&self) -> &String { diff --git a/packages/rs-dpp/src/data_contract/document_type/accessors/v1/mod.rs b/packages/rs-dpp/src/data_contract/document_type/accessors/v1/mod.rs new file mode 100644 index 00000000000..e17ec2f1e5f --- /dev/null +++ b/packages/rs-dpp/src/data_contract/document_type/accessors/v1/mod.rs @@ -0,0 +1,47 @@ +use crate::balances::credits::TokenAmount; +use crate::data_contract::TokenContractPosition; + +/// Trait providing getters for retrieving token costs associated with different document operations. +pub trait DocumentTypeV1Getters { + /// Returns the token cost associated with document creation, if applicable. + /// + /// # Returns + /// - `Some((TokenContractPosition, TokenAmount))` if a creation cost exists. + /// - `None` if no cost is set for document creation. + fn document_creation_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; + + /// Returns the token cost associated with document replacement (updating), if applicable. + /// + /// # Returns + /// - `Some((TokenContractPosition, TokenAmount))` if a replacement cost exists. + /// - `None` if no cost is set for document replacement. + fn document_replacement_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; + + /// Returns the token cost associated with document deletion, if applicable. + /// + /// # Returns + /// - `Some((TokenContractPosition, TokenAmount))` if a deletion cost exists. + /// - `None` if no cost is set for document deletion. + fn document_deletion_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; + + /// Returns the token cost associated with document transfer, if applicable. + /// + /// # Returns + /// - `Some((TokenContractPosition, TokenAmount))` if a transfer cost exists. + /// - `None` if no cost is set for document transfer. + fn document_transfer_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; + + /// Returns the token cost associated with updating the price of a document, if applicable. + /// + /// # Returns + /// - `Some((TokenContractPosition, TokenAmount))` if an update price cost exists. + /// - `None` if no cost is set for updating the price. + fn document_price_update_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; + + /// Returns the token cost associated with document purchase, if applicable. + /// + /// # Returns + /// - `Some((TokenContractPosition, TokenAmount))` if a purchase cost exists. + /// - `None` if no cost is set for document purchase. + fn document_purchase_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; +} 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 1eea1fa1235..5f7cc94c2c1 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 @@ -13,7 +13,7 @@ use crate::data_contract::document_type::property::{DocumentProperty, DocumentPr use crate::data_contract::document_type::schema::validate_max_depth; use crate::data_contract::document_type::v0::DocumentTypeV0; #[cfg(feature = "validation")] -use crate::data_contract::document_type::v0::StatelessJsonSchemaLazyValidator; +use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; use indexmap::IndexMap; #[cfg(feature = "validation")] use std::collections::HashSet; diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/contested_vote_poll_for_document/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/contested_vote_poll_for_document/mod.rs deleted file mode 100644 index e084dffc38f..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/contested_vote_poll_for_document/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod v0; diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/contested_vote_poll_for_document/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/contested_vote_poll_for_document/v0/mod.rs deleted file mode 100644 index ac9911fe5be..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/contested_vote_poll_for_document/v0/mod.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; -use crate::data_contract::document_type::v0::DocumentTypeV0; -use crate::document::{Document, DocumentV0Getters}; -use crate::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll; -use crate::voting::vote_polls::VotePoll; -use platform_value::btreemap_extensions::BTreeValueMapPathHelper; -use platform_value::Value; -use std::collections::BTreeMap; - -impl DocumentTypeV0 { - /// Figures out the prefunded voting balance (v0) for a document in a document type - pub(in crate::data_contract::document_type) fn contested_vote_poll_for_document_v0( - &self, - document: &Document, - ) -> Option { - self.contested_vote_poll_for_document_properties_v0(document.properties()) - } - - pub(in crate::data_contract::document_type) fn contested_vote_poll_for_document_properties_v0( - &self, - document_properties: &BTreeMap, - ) -> Option { - self.indexes() - .values() - .find(|index| { - if let Some(contested_index_info) = &index.contested_index { - contested_index_info - .field_matches - .iter() - .all(|(field, field_match)| { - if let Some(value) = document_properties - .get_optional_at_path(field) - .ok() - .flatten() - { - field_match.matches(value) - } else { - false - } - }) - } else { - false - } - }) - .map(|index| { - let index_values = index.extract_values(document_properties); - VotePoll::ContestedDocumentResourceVotePoll(ContestedDocumentResourceVotePoll { - contract_id: self.data_contract_id, - document_type_name: self.name.clone(), - index_name: index.name.clone(), - index_values, - }) - }) - } -} diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/create_document_from_data/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/create_document_from_data/mod.rs deleted file mode 100644 index e084dffc38f..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/create_document_from_data/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod v0; diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/create_document_from_data/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/create_document_from_data/v0/mod.rs deleted file mode 100644 index 3242e1db19c..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/create_document_from_data/v0/mod.rs +++ /dev/null @@ -1,182 +0,0 @@ -use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; -use crate::data_contract::document_type::methods::DocumentTypeV0Methods; -use crate::data_contract::document_type::v0::DocumentTypeV0; -use crate::document::property_names::{ - CREATED_AT, CREATED_AT_BLOCK_HEIGHT, CREATED_AT_CORE_BLOCK_HEIGHT, TRANSFERRED_AT, - TRANSFERRED_AT_BLOCK_HEIGHT, TRANSFERRED_AT_CORE_BLOCK_HEIGHT, UPDATED_AT, - UPDATED_AT_BLOCK_HEIGHT, UPDATED_AT_CORE_BLOCK_HEIGHT, -}; -use crate::document::{Document, DocumentV0, INITIAL_REVISION}; -use crate::identity::TimestampMillis; -use crate::prelude::{BlockHeight, CoreBlockHeight}; -use crate::version::PlatformVersion; -use crate::ProtocolError; -use chrono::Utc; -use platform_value::btreemap_extensions::BTreeValueMapReplacementPathHelper; -use platform_value::{Identifier, ReplacementType, Value}; - -impl DocumentTypeV0 { - pub(in crate::data_contract::document_type) fn create_document_from_data_v0( - &self, - data: Value, - owner_id: Identifier, - block_height: BlockHeight, - core_block_height: CoreBlockHeight, - document_entropy: [u8; 32], - platform_version: &PlatformVersion, - ) -> Result { - let document_id = Document::generate_document_id_v0( - &self.data_contract_id, - &owner_id, - &self.name, - &document_entropy, - ); - - let revision = if self.requires_revision() { - Some(INITIAL_REVISION) - } else { - None - }; - - // Set timestamps if they are required and not exist - - let mut created_at: Option = data - .get_optional_integer(CREATED_AT) - .map_err(ProtocolError::ValueError)?; - - let mut updated_at: Option = data - .get_optional_integer(UPDATED_AT) - .map_err(ProtocolError::ValueError)?; - - let mut transferred_at: Option = data - .get_optional_integer(TRANSFERRED_AT) - .map_err(ProtocolError::ValueError)?; - - let mut created_at_block_height: Option = data - .get_optional_integer(CREATED_AT_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let mut updated_at_block_height: Option = data - .get_optional_integer(UPDATED_AT_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let mut transferred_at_block_height: Option = data - .get_optional_integer(TRANSFERRED_AT_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let mut created_at_core_block_height: Option = data - .get_optional_integer(CREATED_AT_CORE_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let mut updated_at_core_block_height: Option = data - .get_optional_integer(UPDATED_AT_CORE_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let mut transferred_at_core_block_height: Option = data - .get_optional_integer(TRANSFERRED_AT_CORE_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let is_created_at_required = self.required_fields().contains(CREATED_AT); - let is_updated_at_required = self.required_fields().contains(UPDATED_AT); - let is_transferred_at_required = self.required_fields().contains(TRANSFERRED_AT); - - let is_created_at_block_height_required = - self.required_fields().contains(CREATED_AT_BLOCK_HEIGHT); - let is_updated_at_block_height_required = - self.required_fields().contains(UPDATED_AT_BLOCK_HEIGHT); - let is_transferred_at_block_height_required = - self.required_fields().contains(TRANSFERRED_AT_BLOCK_HEIGHT); - - let is_created_at_core_block_height_required = self - .required_fields() - .contains(CREATED_AT_CORE_BLOCK_HEIGHT); - let is_updated_at_core_block_height_required = self - .required_fields() - .contains(UPDATED_AT_CORE_BLOCK_HEIGHT); - let is_transferred_at_core_block_height_required = self - .required_fields() - .contains(TRANSFERRED_AT_CORE_BLOCK_HEIGHT); - - if (is_created_at_required && created_at.is_none()) - || (is_updated_at_required && updated_at.is_none() - || (is_transferred_at_required && transferred_at.is_none())) - { - //we want only one call to get current time - let now = Utc::now().timestamp_millis() as TimestampMillis; - - if is_created_at_required { - created_at = created_at.or(Some(now)); - }; - - if is_updated_at_required { - updated_at = updated_at.or(Some(now)); - }; - - if is_transferred_at_required { - transferred_at = transferred_at.or(Some(now)); - }; - }; - - if is_created_at_block_height_required { - created_at_block_height = created_at_block_height.or(Some(block_height)); - }; - - if is_updated_at_block_height_required { - updated_at_block_height = updated_at_block_height.or(Some(block_height)); - }; - - if is_transferred_at_block_height_required { - transferred_at_block_height = transferred_at_block_height.or(Some(block_height)); - }; - - if is_created_at_core_block_height_required { - created_at_core_block_height = created_at_core_block_height.or(Some(core_block_height)); - }; - - if is_updated_at_core_block_height_required { - updated_at_core_block_height = updated_at_core_block_height.or(Some(core_block_height)); - }; - - if is_transferred_at_core_block_height_required { - transferred_at_core_block_height = - transferred_at_core_block_height.or(Some(core_block_height)); - }; - - match platform_version - .dpp - .document_versions - .document_structure_version - { - 0 => { - let mut document = DocumentV0 { - id: document_id, - owner_id, - properties: data - .into_btree_string_map() - .map_err(ProtocolError::ValueError)?, - revision, - created_at, - updated_at, - transferred_at, - created_at_block_height, - updated_at_block_height, - transferred_at_block_height, - created_at_core_block_height, - updated_at_core_block_height, - transferred_at_core_block_height, - }; - - document - .properties - .replace_at_paths(self.identifier_paths(), ReplacementType::Identifier)?; - - Ok(document.into()) - } - version => Err(ProtocolError::UnknownVersionMismatch { - method: "convert_value_to_document_v0 inner match to document".to_string(), - known_versions: vec![0], - received: version, - }), - } - } -} diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/create_document_with_prevalidated_properties/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/create_document_with_prevalidated_properties/mod.rs deleted file mode 100644 index e084dffc38f..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/create_document_with_prevalidated_properties/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod v0; diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/create_document_with_prevalidated_properties/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/create_document_with_prevalidated_properties/v0/mod.rs deleted file mode 100644 index d0ab0243e15..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/create_document_with_prevalidated_properties/v0/mod.rs +++ /dev/null @@ -1,175 +0,0 @@ -use crate::document::{Document, DocumentV0}; -use crate::prelude::{BlockHeight, CoreBlockHeight}; -use crate::ProtocolError; -use chrono::Utc; -use platform_value::Value; - -use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; -use crate::data_contract::document_type::methods::DocumentTypeV0Methods; -use crate::data_contract::document_type::v0::DocumentTypeV0; -use crate::document::property_names::{ - CREATED_AT_BLOCK_HEIGHT, CREATED_AT_CORE_BLOCK_HEIGHT, TRANSFERRED_AT, - TRANSFERRED_AT_BLOCK_HEIGHT, TRANSFERRED_AT_CORE_BLOCK_HEIGHT, UPDATED_AT_BLOCK_HEIGHT, - UPDATED_AT_CORE_BLOCK_HEIGHT, -}; -use crate::document::INITIAL_REVISION; -use crate::version::PlatformVersion; -use platform_value::btreemap_extensions::BTreeValueMapHelper; -use platform_value::Identifier; -use std::collections::BTreeMap; - -impl DocumentTypeV0 { - /// Creates a document at the current time based on document type information - /// Properties set here must be pre validated - pub(in crate::data_contract::document_type) fn create_document_with_prevalidated_properties_v0( - &self, - id: Identifier, - owner_id: Identifier, - block_height: BlockHeight, - core_block_height: CoreBlockHeight, - properties: BTreeMap, - platform_version: &PlatformVersion, - ) -> Result { - // Set timestamps if they are required and not exist - let mut created_at: Option = properties - .get_optional_integer(crate::document::property_names::CREATED_AT) - .map_err(ProtocolError::ValueError)?; - - let mut updated_at: Option = properties - .get_optional_integer(crate::document::property_names::UPDATED_AT) - .map_err(ProtocolError::ValueError)?; - - let mut transferred_at: Option = properties - .get_optional_integer(TRANSFERRED_AT) - .map_err(ProtocolError::ValueError)?; - - let mut created_at_block_height: Option = properties - .get_optional_integer(CREATED_AT_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let mut updated_at_block_height: Option = properties - .get_optional_integer(UPDATED_AT_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let mut transferred_at_block_height: Option = properties - .get_optional_integer(TRANSFERRED_AT_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let mut created_at_core_block_height: Option = properties - .get_optional_integer(CREATED_AT_CORE_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let mut updated_at_core_block_height: Option = properties - .get_optional_integer(UPDATED_AT_CORE_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let mut transferred_at_core_block_height: Option = properties - .get_optional_integer(TRANSFERRED_AT_CORE_BLOCK_HEIGHT) - .map_err(ProtocolError::ValueError)?; - - let is_created_at_required = self - .required_fields() - .contains(crate::document::property_names::CREATED_AT); - let is_updated_at_required = self - .required_fields() - .contains(crate::document::property_names::UPDATED_AT); - let is_transferred_at_required = self.required_fields().contains(TRANSFERRED_AT); - - let is_created_at_block_height_required = - self.required_fields().contains(CREATED_AT_BLOCK_HEIGHT); - let is_updated_at_block_height_required = - self.required_fields().contains(UPDATED_AT_BLOCK_HEIGHT); - let is_transferred_at_block_height_required = - self.required_fields().contains(TRANSFERRED_AT_BLOCK_HEIGHT); - - let is_created_at_core_block_height_required = self - .required_fields() - .contains(CREATED_AT_CORE_BLOCK_HEIGHT); - let is_updated_at_core_block_height_required = self - .required_fields() - .contains(UPDATED_AT_CORE_BLOCK_HEIGHT); - let is_transferred_at_core_block_height_required = self - .required_fields() - .contains(TRANSFERRED_AT_CORE_BLOCK_HEIGHT); - - if (is_created_at_required && created_at.is_none()) - || (is_updated_at_required && updated_at.is_none() - || (is_transferred_at_required && transferred_at.is_none())) - { - //we want only one call to get current time - let now = Utc::now().timestamp_millis() as crate::identity::TimestampMillis; - - if is_created_at_required { - created_at = created_at.or(Some(now)); - }; - - if is_updated_at_required { - updated_at = updated_at.or(Some(now)); - }; - - if is_transferred_at_required { - transferred_at = transferred_at.or(Some(now)); - }; - }; - - if is_created_at_block_height_required { - created_at_block_height = created_at_block_height.or(Some(block_height)); - }; - - if is_updated_at_block_height_required { - updated_at_block_height = updated_at_block_height.or(Some(block_height)); - }; - - if is_transferred_at_block_height_required { - transferred_at_block_height = transferred_at_block_height.or(Some(block_height)); - }; - - if is_created_at_core_block_height_required { - created_at_core_block_height = created_at_core_block_height.or(Some(core_block_height)); - }; - - if is_updated_at_core_block_height_required { - updated_at_core_block_height = updated_at_core_block_height.or(Some(core_block_height)); - }; - - if is_transferred_at_core_block_height_required { - transferred_at_core_block_height = - transferred_at_core_block_height.or(Some(core_block_height)); - }; - - let revision = if self.requires_revision() { - Some(INITIAL_REVISION) - } else { - None - }; - - match platform_version - .dpp - .document_versions - .document_structure_version - { - 0 => Ok(DocumentV0 { - id, - owner_id, - properties, - revision, - created_at, - updated_at, - transferred_at, - created_at_block_height, - updated_at_block_height, - transferred_at_block_height, - created_at_core_block_height, - updated_at_core_block_height, - transferred_at_core_block_height, - } - .into()), - version => Err(ProtocolError::UnknownVersionMismatch { - method: "create_document_with_prevalidated_properties_v0 (for document version)" - .to_string(), - known_versions: vec![0], - received: version, - }), - } - } -} diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/deserialize_value_for_key/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/deserialize_value_for_key/mod.rs deleted file mode 100644 index e084dffc38f..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/deserialize_value_for_key/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod v0; diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/deserialize_value_for_key/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/deserialize_value_for_key/v0/mod.rs deleted file mode 100644 index cfba305c39b..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/deserialize_value_for_key/v0/mod.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::data_contract::document_type::v0::DocumentTypeV0; -use crate::data_contract::document_type::DocumentPropertyType; -use crate::data_contract::errors::DataContractError; -use crate::ProtocolError; -use itertools::Itertools; -use platform_value::{Identifier, Value}; - -// If another document type (like V1) ever were to exist we would need to implement serialize_value_for_key_v0 again - -impl DocumentTypeV0 { - pub(in crate::data_contract::document_type) fn deserialize_value_for_key_v0( - &self, - key: &str, - value: &[u8], - ) -> Result { - match key { - "$ownerId" | "$id" => { - let bytes = Identifier::from_bytes(value)?; - Ok(Value::Identifier(bytes.to_buffer())) - } - "$createdAt" | "$updatedAt" | "$transferredAt" => Ok(Value::U64( - DocumentPropertyType::decode_date_timestamp(value).ok_or( - ProtocolError::DataContractError(DataContractError::FieldRequirementUnmet( - "value must be 8 bytes long".to_string(), - )), - )?, - )), - "$createdAtBlockHeight" | "$updatedAtBlockHeight" | "$transferredAtBlockHeight" => { - Ok(Value::U64(DocumentPropertyType::decode_u64(value).ok_or( - ProtocolError::DataContractError(DataContractError::FieldRequirementUnmet( - "value must be 8 bytes long".to_string(), - )), - )?)) - } - "$createdAtCoreBlockHeight" - | "$updatedAtCoreBlockHeight" - | "$transferredAtCoreBlockHeight" => { - Ok(Value::U32(DocumentPropertyType::decode_u32(value).ok_or( - ProtocolError::DataContractError(DataContractError::FieldRequirementUnmet( - "value must be 4 bytes long".to_string(), - )), - )?)) - } - _ => { - let property = self.flattened_properties.get(key).ok_or_else(|| { - DataContractError::DocumentTypeFieldNotFound(format!("expected contract to have field: {key}, contract fields are {} on document type {}", self.flattened_properties.keys().join(" | "), self.name)) - })?; - property.property_type.decode_value_for_tree_keys(value) - } - } - } -} diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/estimated_size/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/estimated_size/mod.rs deleted file mode 100644 index e084dffc38f..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/estimated_size/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod v0; diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/estimated_size/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/estimated_size/v0/mod.rs deleted file mode 100644 index 55e6065f9ea..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/estimated_size/v0/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::data_contract::document_type::v0::DocumentTypeV0; -use crate::ProtocolError; -use platform_version::version::PlatformVersion; -// If another document type (like V1) ever were to exist we would need to implement estimated_size_v0 again - -impl DocumentTypeV0 { - /// The estimated size uses the middle ceil size of all attributes - pub(in crate::data_contract::document_type) fn estimated_size_v0( - &self, - platform_version: &PlatformVersion, - ) -> Result { - let mut total_size = 0u16; - - for (_, document_property) in self.flattened_properties.iter() { - // This call now returns a Result, ProtocolError>. - let maybe_size = document_property - .property_type - .middle_byte_size_ceil(platform_version)?; - - if let Some(size) = maybe_size { - total_size = match total_size.checked_add(size) { - Some(new_total) => new_total, - None => { - return Ok(u16::MAX); - } - }; - } - } - - Ok(total_size) - } -} diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/index_for_types/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/index_for_types/mod.rs deleted file mode 100644 index e084dffc38f..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/index_for_types/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod v0; diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/index_for_types/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/index_for_types/v0/mod.rs deleted file mode 100644 index 72c582b8906..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/index_for_types/v0/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::data_contract::document_type::index::Index; -use crate::data_contract::document_type::v0::DocumentTypeV0; - -// If another document type ever were to exist we would need to implement index_for_types_v0 again - -impl DocumentTypeV0 { - pub(in crate::data_contract::document_type) fn index_for_types_v0( - &self, - index_names: &[&str], - in_field_name: Option<&str>, - order_by: &[&str], - ) -> Option<(&Index, u16)> { - let mut best_index: Option<(&Index, u16)> = None; - let mut best_difference = u16::MAX; - for (_, index) in self.indices.iter() { - let difference_option = index.matches(index_names, in_field_name, order_by); - if let Some(difference) = difference_option { - if difference == 0 { - return Some((index, 0)); - } else if difference < best_difference { - best_difference = difference; - best_index = Some((index, best_difference)); - } - } - } - best_index - } -} diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/max_size/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/max_size/mod.rs deleted file mode 100644 index e084dffc38f..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/max_size/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod v0; diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/max_size/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/max_size/v0/mod.rs deleted file mode 100644 index 346a7b5cf98..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/max_size/v0/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::data_contract::document_type::v0::DocumentTypeV0; -use crate::ProtocolError; -use platform_version::version::PlatformVersion; -// If another document type (like V1) ever were to exist we would need to implement max_size_v0 again - -impl DocumentTypeV0 { - pub(in crate::data_contract::document_type) fn max_size_v0( - &self, - platform_version: &PlatformVersion, - ) -> Result { - let mut total_size = 0u16; - - for (_, document_property) in self.flattened_properties.iter() { - let maybe_size = document_property - .property_type - .max_byte_size(platform_version)?; - - if let Some(size) = maybe_size { - total_size = match total_size.checked_add(size) { - Some(new_total) => new_total, - None => { - return Ok(u16::MAX); - } - }; - } - } - - Ok(total_size) - } -} diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/mod.rs index d158336994f..04c41d753c9 100644 --- a/packages/rs-dpp/src/data_contract/document_type/methods/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/methods/mod.rs @@ -1,152 +1,77 @@ -mod contested_vote_poll_for_document; -mod create_document_from_data; -mod create_document_with_prevalidated_properties; -mod deserialize_value_for_key; -mod estimated_size; -mod index_for_types; -mod max_size; -mod prefunded_voting_balances_for_document; -mod serialize_value_for_key; #[cfg(feature = "validation")] mod validate_update; +mod versioned_methods; use std::collections::BTreeMap; use crate::data_contract::document_type::index::{Index, IndexProperty}; use crate::data_contract::document_type::index_level::IndexLevel; - -use crate::data_contract::document_type::v0::DocumentTypeV0; use crate::document::Document; use crate::document::INITIAL_REVISION; use crate::prelude::{BlockHeight, CoreBlockHeight, Revision}; use crate::version::PlatformVersion; use crate::ProtocolError; +use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; +use crate::data_contract::document_type::methods::versioned_methods::DocumentTypeV0MethodsVersioned; use crate::fee::Credits; use crate::voting::vote_polls::VotePoll; use platform_value::{Identifier, Value}; -// TODO: Some of those methods are only for tests. Hide under feature -pub trait DocumentTypeV0Methods { - fn index_for_types( - &self, - index_names: &[&str], - in_field_name: Option<&str>, - order_by: &[&str], - platform_version: &PlatformVersion, - ) -> Result, ProtocolError>; - - fn serialize_value_for_key( - &self, - key: &str, - value: &Value, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError>; - fn deserialize_value_for_key( - &self, - key: &str, - serialized_value: &[u8], - platform_version: &PlatformVersion, - ) -> Result; - - fn max_size(&self, platform_version: &PlatformVersion) -> Result; - - fn estimated_size(&self, platform_version: &PlatformVersion) -> Result; - - /// Non versioned - fn unique_id_for_storage(&self) -> [u8; 32]; +pub trait DocumentTypeBasicMethods: DocumentTypeV0Getters { + fn unique_id_for_storage(&self) -> [u8; 32] { + rand::random::<[u8; 32]>() + } - /// Non versioned fn unique_id_for_document_field( &self, index_level: &IndexLevel, base_event: [u8; 32], - ) -> Vec; - - /// Non versioned - fn initial_revision(&self) -> Option; - - /// Non versioned - fn requires_revision(&self) -> bool; - - /// Non versioned - fn top_level_indices(&self) -> Vec<&IndexProperty>; - - /// Non versioned - fn top_level_indices_of_contested_unique_indexes(&self) -> Vec<&IndexProperty>; + ) -> Vec { + let mut bytes = index_level.identifier().to_be_bytes().to_vec(); + bytes.extend_from_slice(&base_event); + bytes + } - fn create_document_from_data( - &self, - data: Value, - owner_id: Identifier, - block_height: BlockHeight, - core_block_height: CoreBlockHeight, - document_entropy: [u8; 32], - platform_version: &PlatformVersion, - ) -> Result; + fn initial_revision(&self) -> Option { + if self.requires_revision() { + Some(INITIAL_REVISION) + } else { + None + } + } - /// Creates a document at the current time based on specified document type information. - /// This function requires that all properties provided are pre-validated according to - /// the document's schema requirements. - /// - /// # Parameters: - /// - `id`: An identifier for the document. Unique within the context of the document's type. - /// - `owner_id`: The identifier of the entity that will own this document. - /// - `block_height`: The block height at which this document is considered to have been created. - /// While this value is recorded in the document, it is ignored when the document is broadcasted - /// to the network. This is because the actual block height at the time of broadcast may differ. - /// This parameter is included to fulfill schema requirements that specify a block height; you may - /// use the current block height, a placeholder value of 0, or any other value as necessary. - /// - `core_block_height`: Similar to `block_height`, this represents the core network's block height - /// at the document's creation time. It is handled the same way as `block_height` regarding broadcast - /// and schema requirements. - /// - `properties`: A collection of properties for the document, structured as a `BTreeMap`. - /// These must be pre-validated to match the document's schema definitions. - /// - `platform_version`: A reference to the current version of the platform for which the document is created. - /// - /// # Returns: - /// A `Result`, which is `Ok` if the document was successfully created, or an error - /// indicating what went wrong during the creation process. - /// - /// # Note: - /// The `block_height` and `core_block_height` are primarily included for schema compliance and local record-keeping. - /// These values are not used when the document is broadcasted to the network, as the network assigns its own block - /// heights upon receipt and processing of the document. After broadcasting, it is recommended to update these fields - /// in their created_at/updated_at variants as well as the base created_at/updated_at in the client-side - /// representation of the document to reflect the values returned by the network. The base created_at/updated_at - /// uses current time when creating the local document and is also ignored as it is also set network side. - fn create_document_with_prevalidated_properties( - &self, - id: Identifier, - owner_id: Identifier, - block_height: BlockHeight, - core_block_height: CoreBlockHeight, - properties: BTreeMap, - platform_version: &PlatformVersion, - ) -> Result; + fn requires_revision(&self) -> bool { + self.documents_mutable() + || self.documents_transferable().is_transferable() + || self.trade_mode().seller_sets_price() + } - /// Figures out the minimum prefunded voting balance needed for a document - fn prefunded_voting_balance_for_document( - &self, - document: &Document, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError>; + fn top_level_indices(&self) -> Vec<&IndexProperty> { + self.indexes() + .values() + .filter_map(|index| index.properties.first()) + .collect() + } - /// Gets the vote poll associated with a document - fn contested_vote_poll_for_document( - &self, - document: &Document, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError>; - /// Gets the vote poll associated with a document - fn contested_vote_poll_for_document_properties( - &self, - document_properties: &BTreeMap, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError>; + // This should normally just be 1 item, however we keep a vec in case we want to change things + // in the future. + fn top_level_indices_of_contested_unique_indexes(&self) -> Vec<&IndexProperty> { + self.indexes() + .values() + .filter_map(|index| { + if index.contested_index.is_some() { + index.properties.first() + } else { + None + } + }) + .collect() + } } -impl DocumentTypeV0Methods for DocumentTypeV0 { +// TODO: Some of those methods are only for tests. Hide under feature +pub trait DocumentTypeV0Methods: DocumentTypeV0Getters + DocumentTypeV0MethodsVersioned { fn index_for_types( &self, index_names: &[&str], @@ -191,11 +116,10 @@ impl DocumentTypeV0Methods for DocumentTypeV0 { }), } } - fn deserialize_value_for_key( &self, key: &str, - value: &[u8], + serialized_value: &[u8], platform_version: &PlatformVersion, ) -> Result { match platform_version @@ -205,7 +129,7 @@ impl DocumentTypeV0Methods for DocumentTypeV0 { .methods .deserialize_value_for_key { - 0 => self.deserialize_value_for_key_v0(key, value), + 0 => self.deserialize_value_for_key_v0(key, serialized_value), version => Err(ProtocolError::UnknownVersionMismatch { method: "deserialize_value_for_key".to_string(), known_versions: vec![0], @@ -248,57 +172,6 @@ impl DocumentTypeV0Methods for DocumentTypeV0 { } } - // TODO: Super wired. Remove from here. - fn unique_id_for_storage(&self) -> [u8; 32] { - rand::random::<[u8; 32]>() - } - - fn unique_id_for_document_field( - &self, - index_level: &IndexLevel, - base_event: [u8; 32], - ) -> Vec { - let mut bytes = index_level.identifier().to_be_bytes().to_vec(); - bytes.extend_from_slice(&base_event); - bytes - } - - fn initial_revision(&self) -> Option { - if self.requires_revision() { - Some(INITIAL_REVISION) - } else { - None - } - } - - fn requires_revision(&self) -> bool { - self.documents_mutable - || self.documents_transferable.is_transferable() - || self.trade_mode.seller_sets_price() - } - - fn top_level_indices(&self) -> Vec<&IndexProperty> { - self.indices - .values() - .filter_map(|index| index.properties.first()) - .collect() - } - - // This should normally just be 1 item, however we keep a vec in case we want to change things - // in the future. - fn top_level_indices_of_contested_unique_indexes(&self) -> Vec<&IndexProperty> { - self.indices - .values() - .filter_map(|index| { - if index.contested_index.is_some() { - index.properties.first() - } else { - None - } - }) - .collect() - } - fn create_document_from_data( &self, data: Value, @@ -331,6 +204,36 @@ impl DocumentTypeV0Methods for DocumentTypeV0 { } } + /// Creates a document at the current time based on specified document type information. + /// This function requires that all properties provided are pre-validated according to + /// the document's schema requirements. + /// + /// # Parameters: + /// - `id`: An identifier for the document. Unique within the context of the document's type. + /// - `owner_id`: The identifier of the entity that will own this document. + /// - `block_height`: The block height at which this document is considered to have been created. + /// While this value is recorded in the document, it is ignored when the document is broadcasted + /// to the network. This is because the actual block height at the time of broadcast may differ. + /// This parameter is included to fulfill schema requirements that specify a block height; you may + /// use the current block height, a placeholder value of 0, or any other value as necessary. + /// - `core_block_height`: Similar to `block_height`, this represents the core network's block height + /// at the document's creation time. It is handled the same way as `block_height` regarding broadcast + /// and schema requirements. + /// - `properties`: A collection of properties for the document, structured as a `BTreeMap`. + /// These must be pre-validated to match the document's schema definitions. + /// - `platform_version`: A reference to the current version of the platform for which the document is created. + /// + /// # Returns: + /// A `Result`, which is `Ok` if the document was successfully created, or an error + /// indicating what went wrong during the creation process. + /// + /// # Note: + /// The `block_height` and `core_block_height` are primarily included for schema compliance and local record-keeping. + /// These values are not used when the document is broadcasted to the network, as the network assigns its own block + /// heights upon receipt and processing of the document. After broadcasting, it is recommended to update these fields + /// in their created_at/updated_at variants as well as the base created_at/updated_at in the client-side + /// representation of the document to reflect the values returned by the network. The base created_at/updated_at + /// uses current time when creating the local document and is also ignored as it is also set network side. fn create_document_with_prevalidated_properties( &self, id: Identifier, @@ -363,6 +266,7 @@ impl DocumentTypeV0Methods for DocumentTypeV0 { } } + /// Figures out the minimum prefunded voting balance needed for a document fn prefunded_voting_balance_for_document( &self, document: &Document, @@ -405,7 +309,6 @@ impl DocumentTypeV0Methods for DocumentTypeV0 { }), } } - /// Gets the vote poll associated with a document fn contested_vote_poll_for_document_properties( &self, diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/prefunded_voting_balances_for_document/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/prefunded_voting_balances_for_document/mod.rs deleted file mode 100644 index e084dffc38f..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/prefunded_voting_balances_for_document/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod v0; diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/prefunded_voting_balances_for_document/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/prefunded_voting_balances_for_document/v0/mod.rs deleted file mode 100644 index afffeb1a68f..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/prefunded_voting_balances_for_document/v0/mod.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; -use crate::data_contract::document_type::v0::DocumentTypeV0; -use crate::document::{Document, DocumentV0Getters}; -use crate::fee::Credits; -use crate::version::PlatformVersion; - -impl DocumentTypeV0 { - /// Figures out the prefunded voting balance (v0) for a document in a document type - pub(in crate::data_contract::document_type) fn prefunded_voting_balance_for_document_v0( - &self, - document: &Document, - platform_version: &PlatformVersion, - ) -> Option<(String, Credits)> { - self.indexes() - .values() - .find(|index| { - if let Some(contested_index_info) = &index.contested_index { - contested_index_info - .field_matches - .iter() - .all(|(field, field_match)| { - if let Some(value) = document.get(field) { - field_match.matches(value) - } else { - false - } - }) - } else { - false - } - }) - .map(|index| { - ( - index.name.clone(), - platform_version - .fee_version - .vote_resolution_fund_fees - .contested_document_vote_resolution_fund_required_amount, - ) - }) - } -} diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/serialize_value_for_key/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/serialize_value_for_key/mod.rs deleted file mode 100644 index e084dffc38f..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/serialize_value_for_key/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod v0; diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/serialize_value_for_key/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/methods/serialize_value_for_key/v0/mod.rs deleted file mode 100644 index 92890cb647a..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/methods/serialize_value_for_key/v0/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::data_contract::document_type::v0::{DocumentTypeV0, DEFAULT_HASH_SIZE, MAX_INDEX_SIZE}; -use crate::data_contract::document_type::DocumentPropertyType; -use crate::data_contract::errors::DataContractError; -use crate::ProtocolError; -use itertools::Itertools; -use platform_value::Value; - -// If another document type (like V1) ever were to exist we would need to implement serialize_value_for_key_v0 again - -impl DocumentTypeV0 { - pub(in crate::data_contract::document_type) fn serialize_value_for_key_v0( - &self, - key: &str, - value: &Value, - ) -> Result, ProtocolError> { - match key { - "$ownerId" | "$id" => { - let bytes = value - .to_identifier_bytes() - .map_err(ProtocolError::ValueError)?; - if bytes.len() != DEFAULT_HASH_SIZE { - Err(ProtocolError::DataContractError( - DataContractError::FieldRequirementUnmet( - "expected system value to be 32 bytes long".to_string(), - ), - )) - } else { - Ok(bytes) - } - } - "$createdAt" | "$updatedAt" | "$transferredAt" => { - Ok(DocumentPropertyType::encode_date_timestamp( - value.to_integer().map_err(ProtocolError::ValueError)?, - )) - } - "$createdAtBlockHeight" | "$updatedAtBlockHeight" | "$transferredAtBlockHeight" => { - Ok(DocumentPropertyType::encode_u64( - value.to_integer().map_err(ProtocolError::ValueError)?, - )) - } - "$createdAtCoreBlockHeight" - | "$updatedAtCoreBlockHeight" - | "$transferredAtCoreBlockHeight" => Ok(DocumentPropertyType::encode_u32( - value.to_integer().map_err(ProtocolError::ValueError)?, - )), - _ => { - let property = self.flattened_properties.get(key).ok_or_else(|| { - DataContractError::DocumentTypeFieldNotFound(format!("expected contract to have field: {key}, contract fields are {} on document type {}", self.flattened_properties.keys().join(" | "), self.name)) - })?; - let bytes = property.property_type.encode_value_for_tree_keys(value)?; - if bytes.len() > MAX_INDEX_SIZE { - Err(ProtocolError::DataContractError( - DataContractError::FieldRequirementUnmet( - "value must be less than 256 bytes long".to_string(), - ), - )) - } else { - Ok(bytes) - } - } - } - } -} diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/versioned_methods.rs b/packages/rs-dpp/src/data_contract/document_type/methods/versioned_methods.rs new file mode 100644 index 00000000000..b742618bf42 --- /dev/null +++ b/packages/rs-dpp/src/data_contract/document_type/methods/versioned_methods.rs @@ -0,0 +1,591 @@ +use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; +use crate::data_contract::document_type::methods::DocumentTypeBasicMethods; +use crate::data_contract::document_type::v0::DocumentTypeV0; +use crate::data_contract::document_type::v1::DocumentTypeV1; +use crate::data_contract::document_type::{ + DocumentPropertyType, DocumentType, DocumentTypeRef, Index, DEFAULT_HASH_SIZE, MAX_INDEX_SIZE, +}; +use crate::data_contract::errors::DataContractError; +use crate::document::property_names::{ + CREATED_AT, CREATED_AT_BLOCK_HEIGHT, CREATED_AT_CORE_BLOCK_HEIGHT, TRANSFERRED_AT, + TRANSFERRED_AT_BLOCK_HEIGHT, TRANSFERRED_AT_CORE_BLOCK_HEIGHT, UPDATED_AT, + UPDATED_AT_BLOCK_HEIGHT, UPDATED_AT_CORE_BLOCK_HEIGHT, +}; +use crate::document::{Document, DocumentV0, DocumentV0Getters, INITIAL_REVISION}; +use crate::fee::Credits; +use crate::identity::TimestampMillis; +use crate::prelude::{BlockHeight, CoreBlockHeight}; +use crate::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll; +use crate::voting::vote_polls::VotePoll; +use crate::ProtocolError; +use chrono::Utc; +use itertools::Itertools; +use platform_value::btreemap_extensions::{ + BTreeValueMapHelper, BTreeValueMapPathHelper, BTreeValueMapReplacementPathHelper, +}; +use platform_value::{Identifier, ReplacementType, Value}; +use platform_version::version::PlatformVersion; +use std::collections::BTreeMap; + +pub(super) trait DocumentTypeV0MethodsVersioned: + DocumentTypeV0Getters + DocumentTypeBasicMethods +{ + fn create_document_from_data_v0( + &self, + data: Value, + owner_id: Identifier, + block_height: BlockHeight, + core_block_height: CoreBlockHeight, + document_entropy: [u8; 32], + platform_version: &PlatformVersion, + ) -> Result { + let document_id = Document::generate_document_id_v0( + &self.data_contract_id(), + &owner_id, + self.name(), + &document_entropy, + ); + + let revision = if self.requires_revision() { + Some(INITIAL_REVISION) + } else { + None + }; + + // Set timestamps if they are required and not exist + + let mut created_at: Option = data + .get_optional_integer(CREATED_AT) + .map_err(ProtocolError::ValueError)?; + + let mut updated_at: Option = data + .get_optional_integer(UPDATED_AT) + .map_err(ProtocolError::ValueError)?; + + let mut transferred_at: Option = data + .get_optional_integer(TRANSFERRED_AT) + .map_err(ProtocolError::ValueError)?; + + let mut created_at_block_height: Option = data + .get_optional_integer(CREATED_AT_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let mut updated_at_block_height: Option = data + .get_optional_integer(UPDATED_AT_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let mut transferred_at_block_height: Option = data + .get_optional_integer(TRANSFERRED_AT_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let mut created_at_core_block_height: Option = data + .get_optional_integer(CREATED_AT_CORE_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let mut updated_at_core_block_height: Option = data + .get_optional_integer(UPDATED_AT_CORE_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let mut transferred_at_core_block_height: Option = data + .get_optional_integer(TRANSFERRED_AT_CORE_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let is_created_at_required = self.required_fields().contains(CREATED_AT); + let is_updated_at_required = self.required_fields().contains(UPDATED_AT); + let is_transferred_at_required = self.required_fields().contains(TRANSFERRED_AT); + + let is_created_at_block_height_required = + self.required_fields().contains(CREATED_AT_BLOCK_HEIGHT); + let is_updated_at_block_height_required = + self.required_fields().contains(UPDATED_AT_BLOCK_HEIGHT); + let is_transferred_at_block_height_required = + self.required_fields().contains(TRANSFERRED_AT_BLOCK_HEIGHT); + + let is_created_at_core_block_height_required = self + .required_fields() + .contains(CREATED_AT_CORE_BLOCK_HEIGHT); + let is_updated_at_core_block_height_required = self + .required_fields() + .contains(UPDATED_AT_CORE_BLOCK_HEIGHT); + let is_transferred_at_core_block_height_required = self + .required_fields() + .contains(TRANSFERRED_AT_CORE_BLOCK_HEIGHT); + + if (is_created_at_required && created_at.is_none()) + || (is_updated_at_required && updated_at.is_none() + || (is_transferred_at_required && transferred_at.is_none())) + { + //we want only one call to get current time + let now = Utc::now().timestamp_millis() as TimestampMillis; + + if is_created_at_required { + created_at = created_at.or(Some(now)); + }; + + if is_updated_at_required { + updated_at = updated_at.or(Some(now)); + }; + + if is_transferred_at_required { + transferred_at = transferred_at.or(Some(now)); + }; + }; + + if is_created_at_block_height_required { + created_at_block_height = created_at_block_height.or(Some(block_height)); + }; + + if is_updated_at_block_height_required { + updated_at_block_height = updated_at_block_height.or(Some(block_height)); + }; + + if is_transferred_at_block_height_required { + transferred_at_block_height = transferred_at_block_height.or(Some(block_height)); + }; + + if is_created_at_core_block_height_required { + created_at_core_block_height = created_at_core_block_height.or(Some(core_block_height)); + }; + + if is_updated_at_core_block_height_required { + updated_at_core_block_height = updated_at_core_block_height.or(Some(core_block_height)); + }; + + if is_transferred_at_core_block_height_required { + transferred_at_core_block_height = + transferred_at_core_block_height.or(Some(core_block_height)); + }; + + match platform_version + .dpp + .document_versions + .document_structure_version + { + 0 => { + let mut document = DocumentV0 { + id: document_id, + owner_id, + properties: data + .into_btree_string_map() + .map_err(ProtocolError::ValueError)?, + revision, + created_at, + updated_at, + transferred_at, + created_at_block_height, + updated_at_block_height, + transferred_at_block_height, + created_at_core_block_height, + updated_at_core_block_height, + transferred_at_core_block_height, + }; + + document + .properties + .replace_at_paths(self.identifier_paths(), ReplacementType::Identifier)?; + + Ok(document.into()) + } + version => Err(ProtocolError::UnknownVersionMismatch { + method: "convert_value_to_document_v0 inner match to document".to_string(), + known_versions: vec![0], + received: version, + }), + } + } + + /// Creates a document at the current time based on document type information + /// Properties set here must be pre validated + fn create_document_with_prevalidated_properties_v0( + &self, + id: Identifier, + owner_id: Identifier, + block_height: BlockHeight, + core_block_height: CoreBlockHeight, + properties: BTreeMap, + platform_version: &PlatformVersion, + ) -> Result { + // Set timestamps if they are required and not exist + let mut created_at: Option = properties + .get_optional_integer(CREATED_AT) + .map_err(ProtocolError::ValueError)?; + + let mut updated_at: Option = properties + .get_optional_integer(UPDATED_AT) + .map_err(ProtocolError::ValueError)?; + + let mut transferred_at: Option = properties + .get_optional_integer(TRANSFERRED_AT) + .map_err(ProtocolError::ValueError)?; + + let mut created_at_block_height: Option = properties + .get_optional_integer(CREATED_AT_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let mut updated_at_block_height: Option = properties + .get_optional_integer(UPDATED_AT_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let mut transferred_at_block_height: Option = properties + .get_optional_integer(TRANSFERRED_AT_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let mut created_at_core_block_height: Option = properties + .get_optional_integer(CREATED_AT_CORE_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let mut updated_at_core_block_height: Option = properties + .get_optional_integer(UPDATED_AT_CORE_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let mut transferred_at_core_block_height: Option = properties + .get_optional_integer(TRANSFERRED_AT_CORE_BLOCK_HEIGHT) + .map_err(ProtocolError::ValueError)?; + + let is_created_at_required = self.required_fields().contains(CREATED_AT); + let is_updated_at_required = self.required_fields().contains(UPDATED_AT); + let is_transferred_at_required = self.required_fields().contains(TRANSFERRED_AT); + + let is_created_at_block_height_required = + self.required_fields().contains(CREATED_AT_BLOCK_HEIGHT); + let is_updated_at_block_height_required = + self.required_fields().contains(UPDATED_AT_BLOCK_HEIGHT); + let is_transferred_at_block_height_required = + self.required_fields().contains(TRANSFERRED_AT_BLOCK_HEIGHT); + + let is_created_at_core_block_height_required = self + .required_fields() + .contains(CREATED_AT_CORE_BLOCK_HEIGHT); + let is_updated_at_core_block_height_required = self + .required_fields() + .contains(UPDATED_AT_CORE_BLOCK_HEIGHT); + let is_transferred_at_core_block_height_required = self + .required_fields() + .contains(TRANSFERRED_AT_CORE_BLOCK_HEIGHT); + + if (is_created_at_required && created_at.is_none()) + || (is_updated_at_required && updated_at.is_none() + || (is_transferred_at_required && transferred_at.is_none())) + { + //we want only one call to get current time + let now = Utc::now().timestamp_millis() as TimestampMillis; + + if is_created_at_required { + created_at = created_at.or(Some(now)); + }; + + if is_updated_at_required { + updated_at = updated_at.or(Some(now)); + }; + + if is_transferred_at_required { + transferred_at = transferred_at.or(Some(now)); + }; + }; + + if is_created_at_block_height_required { + created_at_block_height = created_at_block_height.or(Some(block_height)); + }; + + if is_updated_at_block_height_required { + updated_at_block_height = updated_at_block_height.or(Some(block_height)); + }; + + if is_transferred_at_block_height_required { + transferred_at_block_height = transferred_at_block_height.or(Some(block_height)); + }; + + if is_created_at_core_block_height_required { + created_at_core_block_height = created_at_core_block_height.or(Some(core_block_height)); + }; + + if is_updated_at_core_block_height_required { + updated_at_core_block_height = updated_at_core_block_height.or(Some(core_block_height)); + }; + + if is_transferred_at_core_block_height_required { + transferred_at_core_block_height = + transferred_at_core_block_height.or(Some(core_block_height)); + }; + + let revision = if self.requires_revision() { + Some(INITIAL_REVISION) + } else { + None + }; + + match platform_version + .dpp + .document_versions + .document_structure_version + { + 0 => Ok(DocumentV0 { + id, + owner_id, + properties, + revision, + created_at, + updated_at, + transferred_at, + created_at_block_height, + updated_at_block_height, + transferred_at_block_height, + created_at_core_block_height, + updated_at_core_block_height, + transferred_at_core_block_height, + } + .into()), + version => Err(ProtocolError::UnknownVersionMismatch { + method: "create_document_with_prevalidated_properties_v0 (for document version)" + .to_string(), + known_versions: vec![0], + received: version, + }), + } + } + + /// Figures out the prefunded voting balance (v0) for a document in a document type + fn contested_vote_poll_for_document_v0(&self, document: &Document) -> Option { + self.contested_vote_poll_for_document_properties_v0(document.properties()) + } + + fn contested_vote_poll_for_document_properties_v0( + &self, + document_properties: &BTreeMap, + ) -> Option { + self.indexes() + .values() + .find(|index| { + if let Some(contested_index_info) = &index.contested_index { + contested_index_info + .field_matches + .iter() + .all(|(field, field_match)| { + if let Some(value) = document_properties + .get_optional_at_path(field) + .ok() + .flatten() + { + field_match.matches(value) + } else { + false + } + }) + } else { + false + } + }) + .map(|index| { + let index_values = index.extract_values(document_properties); + VotePoll::ContestedDocumentResourceVotePoll(ContestedDocumentResourceVotePoll { + contract_id: self.data_contract_id(), + document_type_name: self.name().clone(), + index_name: index.name.clone(), + index_values, + }) + }) + } + + fn index_for_types_v0( + &self, + index_names: &[&str], + in_field_name: Option<&str>, + order_by: &[&str], + ) -> Option<(&Index, u16)> { + let mut best_index: Option<(&Index, u16)> = None; + let mut best_difference = u16::MAX; + for (_, index) in self.indexes().iter() { + let difference_option = index.matches(index_names, in_field_name, order_by); + if let Some(difference) = difference_option { + if difference == 0 { + return Some((index, 0)); + } else if difference < best_difference { + best_difference = difference; + best_index = Some((index, best_difference)); + } + } + } + best_index + } + + /// The estimated size uses the middle ceil size of all attributes + fn estimated_size_v0(&self, platform_version: &PlatformVersion) -> Result { + let mut total_size = 0u16; + + for (_, document_property) in self.flattened_properties().iter() { + // This call now returns a Result, ProtocolError>. + let maybe_size = document_property + .property_type + .middle_byte_size_ceil(platform_version)?; + + if let Some(size) = maybe_size { + total_size = match total_size.checked_add(size) { + Some(new_total) => new_total, + None => { + return Ok(u16::MAX); + } + }; + } + } + + Ok(total_size) + } + + fn max_size_v0(&self, platform_version: &PlatformVersion) -> Result { + let mut total_size = 0u16; + + for (_, document_property) in self.flattened_properties().iter() { + let maybe_size = document_property + .property_type + .max_byte_size(platform_version)?; + + if let Some(size) = maybe_size { + total_size = match total_size.checked_add(size) { + Some(new_total) => new_total, + None => { + return Ok(u16::MAX); + } + }; + } + } + + Ok(total_size) + } + + /// Figures out the prefunded voting balance (v0) for a document in a document type + fn prefunded_voting_balance_for_document_v0( + &self, + document: &Document, + platform_version: &PlatformVersion, + ) -> Option<(String, Credits)> { + self.indexes() + .values() + .find(|index| { + if let Some(contested_index_info) = &index.contested_index { + contested_index_info + .field_matches + .iter() + .all(|(field, field_match)| { + if let Some(value) = document.get(field) { + field_match.matches(value) + } else { + false + } + }) + } else { + false + } + }) + .map(|index| { + ( + index.name.clone(), + platform_version + .fee_version + .vote_resolution_fund_fees + .contested_document_vote_resolution_fund_required_amount, + ) + }) + } + + fn serialize_value_for_key_v0( + &self, + key: &str, + value: &Value, + ) -> Result, ProtocolError> { + match key { + "$ownerId" | "$id" => { + let bytes = value + .to_identifier_bytes() + .map_err(ProtocolError::ValueError)?; + if bytes.len() != DEFAULT_HASH_SIZE { + Err(ProtocolError::DataContractError( + DataContractError::FieldRequirementUnmet( + "expected system value to be 32 bytes long".to_string(), + ), + )) + } else { + Ok(bytes) + } + } + "$createdAt" | "$updatedAt" | "$transferredAt" => { + Ok(DocumentPropertyType::encode_date_timestamp( + value.to_integer().map_err(ProtocolError::ValueError)?, + )) + } + "$createdAtBlockHeight" | "$updatedAtBlockHeight" | "$transferredAtBlockHeight" => { + Ok(DocumentPropertyType::encode_u64( + value.to_integer().map_err(ProtocolError::ValueError)?, + )) + } + "$createdAtCoreBlockHeight" + | "$updatedAtCoreBlockHeight" + | "$transferredAtCoreBlockHeight" => Ok(DocumentPropertyType::encode_u32( + value.to_integer().map_err(ProtocolError::ValueError)?, + )), + _ => { + let property = self.flattened_properties().get(key).ok_or_else(|| { + DataContractError::DocumentTypeFieldNotFound(format!("expected contract to have field: {key}, contract fields are {} on document type {}", self.flattened_properties().keys().join(" | "), self.name())) + })?; + let bytes = property.property_type.encode_value_for_tree_keys(value)?; + if bytes.len() > MAX_INDEX_SIZE { + Err(ProtocolError::DataContractError( + DataContractError::FieldRequirementUnmet( + "value must be less than 256 bytes long".to_string(), + ), + )) + } else { + Ok(bytes) + } + } + } + } + + fn deserialize_value_for_key_v0( + &self, + key: &str, + value: &[u8], + ) -> Result { + match key { + "$ownerId" | "$id" => { + let bytes = Identifier::from_bytes(value)?; + Ok(Value::Identifier(bytes.to_buffer())) + } + "$createdAt" | "$updatedAt" | "$transferredAt" => Ok(Value::U64( + DocumentPropertyType::decode_date_timestamp(value).ok_or( + ProtocolError::DataContractError(DataContractError::FieldRequirementUnmet( + "value must be 8 bytes long".to_string(), + )), + )?, + )), + "$createdAtBlockHeight" | "$updatedAtBlockHeight" | "$transferredAtBlockHeight" => { + Ok(Value::U64(DocumentPropertyType::decode_u64(value).ok_or( + ProtocolError::DataContractError(DataContractError::FieldRequirementUnmet( + "value must be 8 bytes long".to_string(), + )), + )?)) + } + "$createdAtCoreBlockHeight" + | "$updatedAtCoreBlockHeight" + | "$transferredAtCoreBlockHeight" => { + Ok(Value::U32(DocumentPropertyType::decode_u32(value).ok_or( + ProtocolError::DataContractError(DataContractError::FieldRequirementUnmet( + "value must be 4 bytes long".to_string(), + )), + )?)) + } + _ => { + let property = self.flattened_properties().get(key).ok_or_else(|| { + DataContractError::DocumentTypeFieldNotFound(format!("expected contract to have field: {key}, contract fields are {} on document type {}", self.flattened_properties().keys().join(" | "), self.name())) + })?; + property.property_type.decode_value_for_tree_keys(value) + } + } + } +} + +impl DocumentTypeV0MethodsVersioned for DocumentTypeV0 {} + +impl DocumentTypeV0MethodsVersioned for DocumentTypeV1 {} +impl DocumentTypeV0MethodsVersioned for DocumentType {} + +impl<'a> DocumentTypeV0MethodsVersioned for DocumentTypeRef<'a> {} diff --git a/packages/rs-dpp/src/data_contract/document_type/mod.rs b/packages/rs-dpp/src/data_contract/document_type/mod.rs index fdded490e5e..ac2a7702755 100644 --- a/packages/rs-dpp/src/data_contract/document_type/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/mod.rs @@ -14,19 +14,28 @@ pub use index_level::IndexType; pub mod random_document; pub mod restricted_creation; pub mod schema; + +mod token_costs; pub mod v0; +pub mod v1; +#[cfg(feature = "validation")] +pub(crate) mod validator; -use crate::data_contract::document_type::methods::DocumentTypeV0Methods; +use crate::data_contract::document_type::methods::{ + DocumentTypeBasicMethods, DocumentTypeV0Methods, +}; use crate::data_contract::document_type::v0::DocumentTypeV0; use crate::document::Document; use crate::fee::Credits; -use crate::prelude::{BlockHeight, CoreBlockHeight, Revision}; use crate::version::PlatformVersion; -use crate::voting::vote_polls::VotePoll; use crate::ProtocolError; use derive_more::From; -use platform_value::{Identifier, Value}; -use std::collections::BTreeMap; + +pub const DEFAULT_HASH_SIZE: usize = 32; +pub const DEFAULT_FLOAT_SIZE: usize = 8; +pub const EMPTY_TREE_STORAGE_SIZE: usize = 33; +pub const MAX_INDEX_SIZE: usize = 255; +pub const STORAGE_FLAGS_SIZE: usize = 2; mod property_names { pub const DOCUMENTS_KEEP_HISTORY: &str = "documentsKeepHistory"; @@ -115,170 +124,10 @@ impl<'a> DocumentTypeRef<'a> { } } -impl<'a> DocumentTypeV0Methods for DocumentTypeRef<'a> { - fn index_for_types( - &self, - index_names: &[&str], - in_field_name: Option<&str>, - order_by: &[&str], - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - match self { - DocumentTypeRef::V0(v0) => { - v0.index_for_types(index_names, in_field_name, order_by, platform_version) - } - } - } - - fn serialize_value_for_key( - &self, - key: &str, - value: &Value, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - match self { - DocumentTypeRef::V0(v0) => v0.serialize_value_for_key(key, value, platform_version), - } - } - - fn deserialize_value_for_key( - &self, - key: &str, - serialized_value: &[u8], - platform_version: &PlatformVersion, - ) -> Result { - match self { - DocumentTypeRef::V0(v0) => { - v0.deserialize_value_for_key(key, serialized_value, platform_version) - } - } - } - - fn max_size(&self, platform_version: &PlatformVersion) -> Result { - match self { - DocumentTypeRef::V0(v0) => v0.max_size(platform_version), - } - } - - fn estimated_size(&self, platform_version: &PlatformVersion) -> Result { - match self { - DocumentTypeRef::V0(v0) => v0.estimated_size(platform_version), - } - } - - fn unique_id_for_storage(&self) -> [u8; 32] { - match self { - DocumentTypeRef::V0(v0) => v0.unique_id_for_storage(), - } - } +impl DocumentTypeBasicMethods for DocumentType {} - fn unique_id_for_document_field( - &self, - index_level: &IndexLevel, - base_event: [u8; 32], - ) -> Vec { - match self { - DocumentTypeRef::V0(v0) => v0.unique_id_for_document_field(index_level, base_event), - } - } +impl<'a> DocumentTypeBasicMethods for DocumentTypeRef<'a> {} - fn initial_revision(&self) -> Option { - match self { - DocumentTypeRef::V0(v0) => v0.initial_revision(), - } - } - - fn requires_revision(&self) -> bool { - match self { - DocumentTypeRef::V0(v0) => v0.requires_revision(), - } - } +impl DocumentTypeV0Methods for DocumentType {} - fn top_level_indices(&self) -> Vec<&IndexProperty> { - match self { - DocumentTypeRef::V0(v0) => v0.top_level_indices(), - } - } - - fn top_level_indices_of_contested_unique_indexes(&self) -> Vec<&IndexProperty> { - match self { - DocumentTypeRef::V0(v0) => v0.top_level_indices_of_contested_unique_indexes(), - } - } - - fn create_document_from_data( - &self, - data: Value, - owner_id: Identifier, - block_height: BlockHeight, - core_block_height: CoreBlockHeight, - document_entropy: [u8; 32], - platform_version: &PlatformVersion, - ) -> Result { - match self { - DocumentTypeRef::V0(v0) => v0.create_document_from_data( - data, - owner_id, - block_height, - core_block_height, - document_entropy, - platform_version, - ), - } - } - - fn create_document_with_prevalidated_properties( - &self, - id: Identifier, - owner_id: Identifier, - block_height: BlockHeight, - core_block_height: CoreBlockHeight, - properties: BTreeMap, - platform_version: &PlatformVersion, - ) -> Result { - match self { - DocumentTypeRef::V0(v0) => v0.create_document_with_prevalidated_properties( - id, - owner_id, - block_height, - core_block_height, - properties, - platform_version, - ), - } - } - - fn prefunded_voting_balance_for_document( - &self, - document: &Document, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - match self { - DocumentTypeRef::V0(v0) => { - v0.prefunded_voting_balance_for_document(document, platform_version) - } - } - } - - fn contested_vote_poll_for_document( - &self, - document: &Document, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - match self { - DocumentTypeRef::V0(v0) => { - v0.contested_vote_poll_for_document(document, platform_version) - } - } - } - fn contested_vote_poll_for_document_properties( - &self, - document_properties: &BTreeMap, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - match self { - DocumentTypeRef::V0(v0) => v0 - .contested_vote_poll_for_document_properties(document_properties, platform_version), - } - } -} +impl<'a> DocumentTypeV0Methods for DocumentTypeRef<'a> {} diff --git a/packages/rs-dpp/src/data_contract/document_type/random_document.rs b/packages/rs-dpp/src/data_contract/document_type/random_document.rs index 9f98397ef9c..35003ce63ea 100644 --- a/packages/rs-dpp/src/data_contract/document_type/random_document.rs +++ b/packages/rs-dpp/src/data_contract/document_type/random_document.rs @@ -1,14 +1,22 @@ use bincode::{Decode, Encode}; +use std::time::{SystemTime, UNIX_EPOCH}; -use platform_value::{Bytes32, Identifier}; -use rand::prelude::StdRng; - +use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; +use crate::data_contract::document_type::methods::DocumentTypeV0Methods; use crate::data_contract::document_type::{DocumentType, DocumentTypeRef}; -use crate::document::Document; +use crate::document::property_names::{ + CREATED_AT, CREATED_AT_BLOCK_HEIGHT, CREATED_AT_CORE_BLOCK_HEIGHT, UPDATED_AT, + UPDATED_AT_BLOCK_HEIGHT, UPDATED_AT_CORE_BLOCK_HEIGHT, +}; +use crate::document::{Document, DocumentV0, INITIAL_REVISION}; +use crate::identity::accessors::IdentityGettersV0; use crate::identity::Identity; use crate::prelude::{BlockHeight, CoreBlockHeight, TimestampMillis}; use crate::version::PlatformVersion; use crate::ProtocolError; +use platform_value::{Bytes32, Identifier}; +use rand::prelude::StdRng; +use rand::SeedableRng; #[derive(Clone, Copy, Debug, Eq, PartialEq, Encode, Decode)] pub enum DocumentFieldFillType { @@ -30,7 +38,7 @@ pub enum DocumentFieldFillSize { // TODO The factory is used in benchmark and tests. Probably it should be available under the test feature /// Functions for creating various types of random documents. -pub trait CreateRandomDocument { +pub trait CreateRandomDocument: DocumentTypeV0Getters + DocumentTypeV0Methods { /// Generates a single random document, employing default behavior for document field /// filling where fields that are not required will not be filled (`DoNotFillIfNotRequired`) and /// any fill size that is contractually allowed may be used (`AnyDocumentFillSize`). @@ -48,7 +56,13 @@ pub trait CreateRandomDocument { &self, seed: Option, platform_version: &PlatformVersion, - ) -> Result; + ) -> Result { + let mut rng = match seed { + None => StdRng::from_entropy(), + Some(seed_value) => StdRng::seed_from_u64(seed_value), + }; + self.random_document_with_rng(&mut rng, platform_version) + } /// Generates a single random document using a specified random number generator. Applies default behavior /// for filling document fields, not filling those that are not required and using any fill size allowed by @@ -65,8 +79,22 @@ pub trait CreateRandomDocument { &self, rng: &mut StdRng, platform_version: &PlatformVersion, - ) -> Result; + ) -> Result { + let owner_id = Identifier::random_with_rng(rng); + let entropy = Bytes32::random_with_rng(rng); + self.random_document_with_params( + owner_id, + entropy, + None, + None, + None, + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + rng, + platform_version, + ) + } /// Generates a specified number of random documents, employing default behavior for document field /// filling where fields that are not required will not be filled (`DoNotFillIfNotRequired`) and /// any fill size that is contractually allowed may be used (`AnyDocumentFillSize`). This method @@ -86,7 +114,13 @@ pub trait CreateRandomDocument { count: u32, seed: Option, platform_version: &PlatformVersion, - ) -> Result, ProtocolError>; + ) -> Result, ProtocolError> { + let mut rng = match seed { + None => StdRng::from_entropy(), + Some(seed_value) => StdRng::seed_from_u64(seed_value), + }; + self.random_documents_with_rng(count, &mut rng, platform_version) + } /// Generates a specified number of random documents using a specific random number generator (`rng`). /// Default document field filling behavior is applied, where not required fields are not filled and any @@ -106,7 +140,13 @@ pub trait CreateRandomDocument { count: u32, rng: &mut StdRng, platform_version: &PlatformVersion, - ) -> Result, ProtocolError>; + ) -> Result, ProtocolError> { + let mut vec: Vec = vec![]; + for _i in 0..count { + vec.push(self.random_document_with_rng(rng, platform_version)?); + } + Ok(vec) + } /// Generates a single random document with a specified identifier and entropy values, using a given /// random number generator. Defaults to not filling non-required fields and allowing any fill size, @@ -131,8 +171,19 @@ pub trait CreateRandomDocument { document_field_fill_type: DocumentFieldFillType, document_field_fill_size: DocumentFieldFillSize, platform_version: &PlatformVersion, - ) -> Result; - + ) -> Result { + self.random_document_with_params( + owner_id, + entropy, + None, + None, + None, + document_field_fill_type, + document_field_fill_size, + rng, + platform_version, + ) + } /// Generates a single random document with specified parameters for customization, using a given /// random number generator. Defaults to not filling non-required fields and allowing any fill size /// unless explicitly specified through `document_field_fill_type` and `document_field_fill_size`. @@ -162,7 +213,149 @@ pub trait CreateRandomDocument { document_field_fill_size: DocumentFieldFillSize, rng: &mut StdRng, platform_version: &PlatformVersion, - ) -> Result; + ) -> Result { + let id = Document::generate_document_id_v0( + &self.data_contract_id(), + &owner_id, + self.name().as_str(), + entropy.as_slice(), + ); + // dbg!("gen", hex::encode(id), hex::encode(&self.data_contract_id), hex::encode(&owner_id), self.name.as_str(), hex::encode(entropy.as_slice())); + let properties = self + .properties() + .iter() + .filter_map(|(key, property)| { + if property.required + || document_field_fill_type == DocumentFieldFillType::FillIfNotRequired + { + let value = match document_field_fill_size { + DocumentFieldFillSize::MinDocumentFillSize => { + property.property_type.random_sub_filled_value(rng) + } + DocumentFieldFillSize::MaxDocumentFillSize => { + property.property_type.random_filled_value(rng) + } + DocumentFieldFillSize::AnyDocumentFillSize => { + property.property_type.random_value(rng) + } + }; + Some((key.clone(), value)) + } else { + None + } + }) + .collect(); + + let revision = if self.requires_revision() { + Some(INITIAL_REVISION) + } else { + None + }; + + let created_at = if self.required_fields().contains(CREATED_AT) { + if time_ms.is_some() { + time_ms + } else { + let now = SystemTime::now(); + let duration_since_epoch = + now.duration_since(UNIX_EPOCH).expect("Time went backwards"); + let milliseconds = duration_since_epoch.as_millis() as u64; + Some(milliseconds) + } + } else { + None + }; + + let updated_at = if self.required_fields().contains(UPDATED_AT) { + if time_ms.is_some() { + time_ms + } else if created_at.is_some() { + created_at + } else { + let now = SystemTime::now(); + let duration_since_epoch = + now.duration_since(UNIX_EPOCH).expect("Time went backwards"); + let milliseconds = duration_since_epoch.as_millis() as u64; + Some(milliseconds) + } + } else { + None + }; + + let created_at_block_height = if self.required_fields().contains(CREATED_AT_BLOCK_HEIGHT) { + if block_height.is_some() { + block_height + } else { + Some(0) + } + } else { + None + }; + + let updated_at_block_height = if self.required_fields().contains(UPDATED_AT_BLOCK_HEIGHT) { + if block_height.is_some() { + block_height + } else { + Some(0) + } + } else { + None + }; + + let created_at_core_block_height = if self + .required_fields() + .contains(CREATED_AT_CORE_BLOCK_HEIGHT) + { + if core_block_height.is_some() { + core_block_height + } else { + Some(0) + } + } else { + None + }; + + let updated_at_core_block_height = if self + .required_fields() + .contains(UPDATED_AT_CORE_BLOCK_HEIGHT) + { + if core_block_height.is_some() { + core_block_height + } else { + Some(0) + } + } else { + None + }; + + match platform_version + .dpp + .document_versions + .document_structure_version + { + 0 => Ok(DocumentV0 { + id, + properties, + owner_id, + revision, + created_at, + updated_at, + transferred_at: None, + created_at_block_height, + updated_at_block_height, + transferred_at_block_height: None, + created_at_core_block_height, + updated_at_core_block_height, + transferred_at_core_block_height: None, + } + .into()), + version => Err(ProtocolError::UnknownVersionMismatch { + method: "DocumentTypeV0::random_document_with_params".to_string(), + known_versions: vec![0], + received: version, + }), + } + } /// Generates a specified number of random documents with additional parameters for customization, including /// identities, timestamps, block heights, and specific document field filling strategies. @@ -182,110 +375,6 @@ pub trait CreateRandomDocument { /// A `Result, ProtocolError>` which is `Ok` containing a vector of tuples /// if successful, each tuple consisting of a Document, its associated Identity, and a Bytes32 value, or an error /// if the operation fails. - fn random_documents_with_params<'i>( - &self, - count: u32, - identities: &[&'i Identity], - time_ms: Option, - block_height: Option, - core_block_height: Option, - document_field_fill_type: DocumentFieldFillType, - document_field_fill_size: DocumentFieldFillSize, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError>; -} - -impl CreateRandomDocument for DocumentType { - fn random_document( - &self, - seed: Option, - platform_version: &PlatformVersion, - ) -> Result { - match self { - DocumentType::V0(v0) => v0.random_document(seed, platform_version), - } - } - - fn random_document_with_rng( - &self, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result { - match self { - DocumentType::V0(v0) => v0.random_document_with_rng(rng, platform_version), - } - } - - fn random_documents( - &self, - count: u32, - seed: Option, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - match self { - DocumentType::V0(v0) => v0.random_documents(count, seed, platform_version), - } - } - - fn random_documents_with_rng( - &self, - count: u32, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - match self { - DocumentType::V0(v0) => v0.random_documents_with_rng(count, rng, platform_version), - } - } - - fn random_document_with_identifier_and_entropy( - &self, - rng: &mut StdRng, - owner_id: Identifier, - entropy: Bytes32, - document_field_fill_type: DocumentFieldFillType, - document_field_fill_size: DocumentFieldFillSize, - platform_version: &PlatformVersion, - ) -> Result { - match self { - DocumentType::V0(v0) => v0.random_document_with_identifier_and_entropy( - rng, - owner_id, - entropy, - document_field_fill_type, - document_field_fill_size, - platform_version, - ), - } - } - - fn random_document_with_params( - &self, - owner_id: Identifier, - entropy: Bytes32, - time_ms: Option, - block_height: Option, - core_block_height: Option, - document_field_fill_type: DocumentFieldFillType, - document_field_fill_size: DocumentFieldFillSize, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result { - match self { - DocumentType::V0(v0) => v0.random_document_with_params( - owner_id, - entropy, - time_ms, - block_height, - core_block_height, - document_field_fill_type, - document_field_fill_size, - rng, - platform_version, - ), // Add more cases as necessary for other variants - } - } fn random_documents_with_params<'i>( &self, count: u32, @@ -298,137 +387,37 @@ impl CreateRandomDocument for DocumentType { rng: &mut StdRng, platform_version: &PlatformVersion, ) -> Result, ProtocolError> { - match self { - DocumentType::V0(v0) => v0.random_documents_with_params( - count, - identities, - time_ms, - block_height, - core_block_height, - document_field_fill_type, - document_field_fill_size, - rng, - platform_version, - ), - } - } -} - -impl<'a> CreateRandomDocument for DocumentTypeRef<'a> { - fn random_document( - &self, - seed: Option, - platform_version: &PlatformVersion, - ) -> Result { - match self { - DocumentTypeRef::V0(v0) => v0.random_document(seed, platform_version), - } - } + let mut vec = vec![]; - fn random_document_with_rng( - &self, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result { - match self { - DocumentTypeRef::V0(v0) => v0.random_document_with_rng(rng, platform_version), + if identities.len() < count as usize { + return Err(ProtocolError::CorruptedCodeExecution(format!( + "not enough identities to create {count} documents" + ))); } - } - fn random_documents( - &self, - count: u32, - seed: Option, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - match self { - DocumentTypeRef::V0(v0) => v0.random_documents(count, seed, platform_version), - } - } - - fn random_documents_with_rng( - &self, - count: u32, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - match self { - DocumentTypeRef::V0(v0) => v0.random_documents_with_rng(count, rng, platform_version), - } - } - - fn random_document_with_identifier_and_entropy( - &self, - rng: &mut StdRng, - owner_id: Identifier, - entropy: Bytes32, - document_field_fill_type: DocumentFieldFillType, - document_field_fill_size: DocumentFieldFillSize, - platform_version: &PlatformVersion, - ) -> Result { - match self { - DocumentTypeRef::V0(v0) => v0.random_document_with_identifier_and_entropy( - rng, - owner_id, + for i in 0..count { + let identity = identities[i as usize]; + let entropy = Bytes32::random_with_rng(rng); + vec.push(( + self.random_document_with_params( + identity.id(), + entropy, + time_ms, + block_height, + core_block_height, + document_field_fill_type, + document_field_fill_size, + rng, + platform_version, + )?, + identity, entropy, - document_field_fill_type, - document_field_fill_size, - platform_version, - ), + )); } + Ok(vec) } +} - fn random_document_with_params( - &self, - owner_id: Identifier, - entropy: Bytes32, - time_ms: Option, - block_height: Option, - core_block_height: Option, - document_field_fill_type: DocumentFieldFillType, - document_field_fill_size: DocumentFieldFillSize, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result { - match self { - DocumentTypeRef::V0(v0) => v0.random_document_with_params( - owner_id, - entropy, - time_ms, - block_height, - core_block_height, - document_field_fill_type, - document_field_fill_size, - rng, - platform_version, - ), // Add more cases as necessary for other variants - } - } +impl CreateRandomDocument for DocumentType {} - fn random_documents_with_params<'i>( - &self, - count: u32, - identities: &[&'i Identity], - time_ms: Option, - block_height: Option, - core_block_height: Option, - document_field_fill_type: DocumentFieldFillType, - document_field_fill_size: DocumentFieldFillSize, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - match self { - DocumentTypeRef::V0(v0) => v0.random_documents_with_params( - count, - identities, - time_ms, - block_height, - core_block_height, - document_field_fill_type, - document_field_fill_size, - rng, - platform_version, - ), // Add more cases as necessary for other variants - } - } -} +impl<'a> CreateRandomDocument for DocumentTypeRef<'a> {} diff --git a/packages/rs-dpp/src/data_contract/document_type/token_costs/accessors.rs b/packages/rs-dpp/src/data_contract/document_type/token_costs/accessors.rs new file mode 100644 index 00000000000..25482cae5ee --- /dev/null +++ b/packages/rs-dpp/src/data_contract/document_type/token_costs/accessors.rs @@ -0,0 +1,62 @@ +use crate::balances::credits::TokenAmount; +use crate::data_contract::TokenContractPosition; + +/// Trait providing getters for retrieving token costs associated with different operations. +pub trait TokenCostGettersV0 { + /// Returns the token cost associated with document creation, if applicable. + fn document_creation_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; + + /// Returns the token cost associated with document replacement (updating), if applicable. + fn document_replacement_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; + + /// Returns the token cost associated with document deletion, if applicable. + fn document_deletion_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; + + /// Returns the token cost associated with document transfer, if applicable. + fn document_transfer_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; + + /// Returns the token cost associated with updating the price of a document, if applicable. + fn document_price_update_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; + + /// Returns the token cost associated with document purchase, if applicable. + fn document_purchase_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; +} + +/// Trait providing setters for modifying token costs associated with different operations. +pub trait TokenCostSettersV0 { + /// Sets the token cost for document creation. + fn set_document_creation_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ); + + /// Sets the token cost for document replacement (updating). + fn set_document_replacement_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ); + + /// Sets the token cost for document deletion. + fn set_document_deletion_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ); + + /// Sets the token cost for document transfer. + fn set_document_transfer_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ); + + /// Sets the token cost for updating the price of a document. + fn set_document_price_update_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ); + + /// Sets the token cost for document purchase. + fn set_document_purchase_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ); +} diff --git a/packages/rs-dpp/src/data_contract/document_type/token_costs/mod.rs b/packages/rs-dpp/src/data_contract/document_type/token_costs/mod.rs new file mode 100644 index 00000000000..9b6c24f1830 --- /dev/null +++ b/packages/rs-dpp/src/data_contract/document_type/token_costs/mod.rs @@ -0,0 +1,111 @@ +use crate::balances::credits::TokenAmount; +use crate::data_contract::document_type::token_costs::accessors::{ + TokenCostGettersV0, TokenCostSettersV0, +}; +use crate::data_contract::document_type::token_costs::v0::TokenCostsV0; +use crate::data_contract::TokenContractPosition; + +pub(crate) mod accessors; +mod v0; + +/// The token costs for various document operations +#[derive(Debug, PartialEq, Clone)] +pub enum TokenCosts { + /// Version 0 of token costs + V0(TokenCostsV0), +} + +/// Implementation of the `TokenCostGettersV0` trait for `TokenCosts` enum. +impl TokenCostGettersV0 for TokenCosts { + fn document_creation_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + TokenCosts::V0(inner) => inner.document_creation_token_cost(), + } + } + + fn document_replacement_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + TokenCosts::V0(inner) => inner.document_replacement_token_cost(), + } + } + + fn document_deletion_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + TokenCosts::V0(inner) => inner.document_deletion_token_cost(), + } + } + + fn document_transfer_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + TokenCosts::V0(inner) => inner.document_transfer_token_cost(), + } + } + + fn document_price_update_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + TokenCosts::V0(inner) => inner.document_price_update_token_cost(), + } + } + + fn document_purchase_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + TokenCosts::V0(inner) => inner.document_purchase_token_cost(), + } + } +} + +impl TokenCostSettersV0 for TokenCosts { + fn set_document_creation_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + match self { + TokenCosts::V0(inner) => inner.set_document_creation_token_cost(cost), + } + } + + fn set_document_replacement_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + match self { + TokenCosts::V0(inner) => inner.set_document_replacement_token_cost(cost), + } + } + + fn set_document_deletion_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + match self { + TokenCosts::V0(inner) => inner.set_document_deletion_token_cost(cost), + } + } + + fn set_document_transfer_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + match self { + TokenCosts::V0(inner) => inner.set_document_transfer_token_cost(cost), + } + } + + fn set_document_price_update_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + match self { + TokenCosts::V0(inner) => inner.set_document_price_update_token_cost(cost), + } + } + + fn set_document_purchase_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + match self { + TokenCosts::V0(inner) => inner.set_document_purchase_token_cost(cost), + } + } +} diff --git a/packages/rs-dpp/src/data_contract/document_type/token_costs/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/token_costs/v0/mod.rs new file mode 100644 index 00000000000..661ed680872 --- /dev/null +++ b/packages/rs-dpp/src/data_contract/document_type/token_costs/v0/mod.rs @@ -0,0 +1,99 @@ +use crate::balances::credits::TokenAmount; +use crate::data_contract::document_type::token_costs::accessors::{ + TokenCostGettersV0, TokenCostSettersV0, +}; +use crate::data_contract::TokenContractPosition; + +/// Token costs for various document operations. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct TokenCostsV0 { + /// Cost of creating a document. + pub create: Option<(TokenContractPosition, TokenAmount)>, + + /// Cost of replacing a document. + pub replace: Option<(TokenContractPosition, TokenAmount)>, + + /// Cost of deleting a document. + pub delete: Option<(TokenContractPosition, TokenAmount)>, + + /// Cost of transferring a document. + pub transfer: Option<(TokenContractPosition, TokenAmount)>, + + /// Cost of updating the price of a document. + pub update_price: Option<(TokenContractPosition, TokenAmount)>, + + /// Cost of purchasing a document. + pub purchase: Option<(TokenContractPosition, TokenAmount)>, +} + +/// Implementation of the `TokenCostGettersV0` trait for `TokenCostsV0`. +impl TokenCostGettersV0 for TokenCostsV0 { + fn document_creation_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.create.clone() + } + + fn document_replacement_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.replace.clone() + } + + fn document_deletion_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.delete.clone() + } + + fn document_transfer_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.transfer.clone() + } + + fn document_price_update_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.update_price.clone() + } + + fn document_purchase_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.purchase.clone() + } +} + +/// Implementation of the `TokenCostSettersV0` trait for `TokenCostsV0`. +impl TokenCostSettersV0 for TokenCostsV0 { + fn set_document_creation_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + self.create = cost; + } + + fn set_document_replacement_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + self.replace = cost; + } + + fn set_document_deletion_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + self.delete = cost; + } + + fn set_document_transfer_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + self.transfer = cost; + } + + fn set_document_price_update_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + self.update_price = cost; + } + + fn set_document_purchase_token_cost( + &mut self, + cost: Option<(TokenContractPosition, TokenAmount)>, + ) { + self.purchase = cost; + } +} diff --git a/packages/rs-dpp/src/data_contract/document_type/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/v0/mod.rs index 9951c2b638b..a2f2a28b991 100644 --- a/packages/rs-dpp/src/data_contract/document_type/v0/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/v0/mod.rs @@ -6,27 +6,20 @@ use crate::data_contract::document_type::index_level::IndexLevel; use crate::data_contract::document_type::property::DocumentProperty; use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; -#[cfg(feature = "validation")] -pub(in crate::data_contract) use validator::StatelessJsonSchemaLazyValidator; - +use crate::data_contract::document_type::methods::{ + DocumentTypeBasicMethods, DocumentTypeV0Methods, +}; use crate::data_contract::document_type::restricted_creation::CreationRestrictionMode; +#[cfg(feature = "validation")] +use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; use crate::document::transfer::Transferable; use crate::identity::SecurityLevel; use crate::nft::TradeMode; use platform_value::{Identifier, Value}; mod accessors; -#[cfg(feature = "random-documents")] -pub mod random_document; #[cfg(feature = "random-document-types")] pub mod random_document_type; -#[cfg(feature = "validation")] -mod validator; -pub const DEFAULT_HASH_SIZE: usize = 32; -pub const DEFAULT_FLOAT_SIZE: usize = 8; -pub const EMPTY_TREE_STORAGE_SIZE: usize = 33; -pub const MAX_INDEX_SIZE: usize = 255; -pub const STORAGE_FLAGS_SIZE: usize = 2; #[derive(Debug, PartialEq, Clone)] pub struct DocumentTypeV0 { @@ -70,6 +63,10 @@ pub struct DocumentTypeV0 { pub(in crate::data_contract) json_schema_validator: StatelessJsonSchemaLazyValidator, } +impl DocumentTypeBasicMethods for DocumentTypeV0 {} + +impl DocumentTypeV0Methods for DocumentTypeV0 {} + impl DocumentTypeV0 { // Public method to set the data_contract_id pub fn set_data_contract_id(&mut self, new_id: Identifier) { diff --git a/packages/rs-dpp/src/data_contract/document_type/v0/random_document.rs b/packages/rs-dpp/src/data_contract/document_type/v0/random_document.rs deleted file mode 100644 index 66f37338607..00000000000 --- a/packages/rs-dpp/src/data_contract/document_type/v0/random_document.rs +++ /dev/null @@ -1,310 +0,0 @@ -//! Random Documents. -//! -//! This module defines the CreateRandomDocument trait and its functions, which -//! create various types of random documents. -//! -//! - -use platform_value::{Bytes32, Identifier}; -use rand::rngs::StdRng; -use rand::SeedableRng; -use std::time::{SystemTime, UNIX_EPOCH}; - -use crate::data_contract::document_type::methods::DocumentTypeV0Methods; -use crate::data_contract::document_type::random_document::{ - CreateRandomDocument, DocumentFieldFillSize, DocumentFieldFillType, -}; -use crate::data_contract::document_type::v0::DocumentTypeV0; -use crate::document::property_names::{ - CREATED_AT, CREATED_AT_BLOCK_HEIGHT, CREATED_AT_CORE_BLOCK_HEIGHT, UPDATED_AT, - UPDATED_AT_BLOCK_HEIGHT, UPDATED_AT_CORE_BLOCK_HEIGHT, -}; -use crate::document::{Document, DocumentV0, INITIAL_REVISION}; -use crate::identity::accessors::IdentityGettersV0; -use crate::identity::Identity; -use crate::prelude::{BlockHeight, CoreBlockHeight, TimestampMillis}; -use crate::version::PlatformVersion; -use crate::ProtocolError; - -impl CreateRandomDocument for DocumentTypeV0 { - /// Creates a random Document using a seed if given, otherwise entropy. - fn random_document( - &self, - seed: Option, - platform_version: &PlatformVersion, - ) -> Result { - let mut rng = match seed { - None => StdRng::from_entropy(), - Some(seed_value) => StdRng::seed_from_u64(seed_value), - }; - self.random_document_with_rng(&mut rng, platform_version) - } - - /// Creates a document with a random id, owner id, and properties using StdRng. - fn random_document_with_rng( - &self, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result { - let owner_id = Identifier::random_with_rng(rng); - let entropy = Bytes32::random_with_rng(rng); - - self.random_document_with_params( - owner_id, - entropy, - None, - None, - None, - DocumentFieldFillType::FillIfNotRequired, - DocumentFieldFillSize::AnyDocumentFillSize, - rng, - platform_version, - ) - } - - /// Creates `count` Documents with random data using a seed if given, otherwise entropy. - fn random_documents( - &self, - count: u32, - seed: Option, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - let mut rng = match seed { - None => StdRng::from_entropy(), - Some(seed_value) => StdRng::seed_from_u64(seed_value), - }; - self.random_documents_with_rng(count, &mut rng, platform_version) - } - - /// Creates `count` Documents with random data using the random number generator given. - fn random_documents_with_rng( - &self, - count: u32, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - let mut vec: Vec = vec![]; - for _i in 0..count { - vec.push(self.random_document_with_rng(rng, platform_version)?); - } - Ok(vec) - } - - /// Creates a document with a random id, owner id, and properties using StdRng. - fn random_document_with_identifier_and_entropy( - &self, - rng: &mut StdRng, - owner_id: Identifier, - entropy: Bytes32, - document_field_fill_type: DocumentFieldFillType, - document_field_fill_size: DocumentFieldFillSize, - platform_version: &PlatformVersion, - ) -> Result { - self.random_document_with_params( - owner_id, - entropy, - None, - None, - None, - document_field_fill_type, - document_field_fill_size, - rng, - platform_version, - ) - } - - /// Creates a document with a given owner id and entropy, and properties using StdRng. - fn random_document_with_params( - &self, - owner_id: Identifier, - entropy: Bytes32, - time_ms: Option, - block_height: Option, - core_block_height: Option, - document_field_fill_type: DocumentFieldFillType, - document_field_fill_size: DocumentFieldFillSize, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result { - let id = Document::generate_document_id_v0( - &self.data_contract_id, - &owner_id, - self.name.as_str(), - entropy.as_slice(), - ); - // dbg!("gen", hex::encode(id), hex::encode(&self.data_contract_id), hex::encode(&owner_id), self.name.as_str(), hex::encode(entropy.as_slice())); - let properties = self - .properties - .iter() - .filter_map(|(key, property)| { - if property.required - || document_field_fill_type == DocumentFieldFillType::FillIfNotRequired - { - let value = match document_field_fill_size { - DocumentFieldFillSize::MinDocumentFillSize => { - property.property_type.random_sub_filled_value(rng) - } - DocumentFieldFillSize::MaxDocumentFillSize => { - property.property_type.random_filled_value(rng) - } - DocumentFieldFillSize::AnyDocumentFillSize => { - property.property_type.random_value(rng) - } - }; - Some((key.clone(), value)) - } else { - None - } - }) - .collect(); - - let revision = if self.requires_revision() { - Some(INITIAL_REVISION) - } else { - None - }; - - let created_at = if self.required_fields.contains(CREATED_AT) { - if time_ms.is_some() { - time_ms - } else { - let now = SystemTime::now(); - let duration_since_epoch = - now.duration_since(UNIX_EPOCH).expect("Time went backwards"); - let milliseconds = duration_since_epoch.as_millis() as u64; - Some(milliseconds) - } - } else { - None - }; - - let updated_at = if self.required_fields.contains(UPDATED_AT) { - if time_ms.is_some() { - time_ms - } else if created_at.is_some() { - created_at - } else { - let now = SystemTime::now(); - let duration_since_epoch = - now.duration_since(UNIX_EPOCH).expect("Time went backwards"); - let milliseconds = duration_since_epoch.as_millis() as u64; - Some(milliseconds) - } - } else { - None - }; - - let created_at_block_height = if self.required_fields.contains(CREATED_AT_BLOCK_HEIGHT) { - if block_height.is_some() { - block_height - } else { - Some(0) - } - } else { - None - }; - - let updated_at_block_height = if self.required_fields.contains(UPDATED_AT_BLOCK_HEIGHT) { - if block_height.is_some() { - block_height - } else { - Some(0) - } - } else { - None - }; - - let created_at_core_block_height = - if self.required_fields.contains(CREATED_AT_CORE_BLOCK_HEIGHT) { - if core_block_height.is_some() { - core_block_height - } else { - Some(0) - } - } else { - None - }; - - let updated_at_core_block_height = - if self.required_fields.contains(UPDATED_AT_CORE_BLOCK_HEIGHT) { - if core_block_height.is_some() { - core_block_height - } else { - Some(0) - } - } else { - None - }; - - match platform_version - .dpp - .document_versions - .document_structure_version - { - 0 => Ok(DocumentV0 { - id, - properties, - owner_id, - revision, - created_at, - updated_at, - transferred_at: None, - created_at_block_height, - updated_at_block_height, - transferred_at_block_height: None, - created_at_core_block_height, - updated_at_core_block_height, - transferred_at_core_block_height: None, - } - .into()), - version => Err(ProtocolError::UnknownVersionMismatch { - method: "DocumentTypeV0::random_document_with_params".to_string(), - known_versions: vec![0], - received: version, - }), - } - } - - /// Creates `count` Documents with random data using the random number generator given. - fn random_documents_with_params<'i>( - &self, - count: u32, - identities: &[&'i Identity], - time_ms: Option, - block_height: Option, - core_block_height: Option, - document_field_fill_type: DocumentFieldFillType, - document_field_fill_size: DocumentFieldFillSize, - rng: &mut StdRng, - platform_version: &PlatformVersion, - ) -> Result, ProtocolError> { - let mut vec = vec![]; - - if identities.len() < count as usize { - return Err(ProtocolError::CorruptedCodeExecution(format!( - "not enough identities to create {count} documents" - ))); - } - - for i in 0..count { - let identity = identities[i as usize]; - let entropy = Bytes32::random_with_rng(rng); - vec.push(( - self.random_document_with_params( - identity.id(), - entropy, - time_ms, - block_height, - core_block_height, - document_field_fill_type, - document_field_fill_size, - rng, - platform_version, - )?, - identity, - entropy, - )); - } - Ok(vec) - } -} diff --git a/packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs b/packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs new file mode 100644 index 00000000000..05c6437af4f --- /dev/null +++ b/packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs @@ -0,0 +1,138 @@ +use crate::data_contract::document_type::accessors::{ + DocumentTypeV0Getters, DocumentTypeV1Getters, +}; +use crate::data_contract::document_type::index::Index; +use crate::data_contract::document_type::index_level::IndexLevel; +use crate::data_contract::document_type::property::DocumentProperty; + +use platform_value::{Identifier, Value}; + +use crate::balances::credits::TokenAmount; +use crate::data_contract::document_type::restricted_creation::CreationRestrictionMode; +use crate::data_contract::document_type::token_costs::accessors::TokenCostGettersV0; +use crate::data_contract::document_type::v1::DocumentTypeV1; +use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; +use crate::data_contract::TokenContractPosition; +use crate::document::transfer::Transferable; +use crate::identity::SecurityLevel; +use crate::nft::TradeMode; +use indexmap::IndexMap; +use std::collections::{BTreeMap, BTreeSet}; + +impl DocumentTypeV0Getters for DocumentTypeV1 { + fn name(&self) -> &String { + &self.name + } + + fn schema(&self) -> &Value { + &self.schema + } + + fn schema_owned(self) -> Value { + self.schema + } + + fn indexes(&self) -> &BTreeMap { + &self.indices + } + + fn find_contested_index(&self) -> Option<&Index> { + self.indices + .iter() + .find(|(_, index)| index.contested_index.is_some()) + .map(|(_, contested_index)| contested_index) + } + + fn index_structure(&self) -> &IndexLevel { + &self.index_structure + } + + fn flattened_properties(&self) -> &IndexMap { + &self.flattened_properties + } + + fn properties(&self) -> &IndexMap { + &self.properties + } + + fn identifier_paths(&self) -> &BTreeSet { + &self.identifier_paths + } + + fn binary_paths(&self) -> &BTreeSet { + &self.binary_paths + } + + fn required_fields(&self) -> &BTreeSet { + &self.required_fields + } + fn transient_fields(&self) -> &BTreeSet { + &self.transient_fields + } + + fn documents_keep_history(&self) -> bool { + self.documents_keep_history + } + + fn documents_mutable(&self) -> bool { + self.documents_mutable + } + + fn documents_can_be_deleted(&self) -> bool { + self.documents_can_be_deleted + } + + fn documents_transferable(&self) -> Transferable { + self.documents_transferable + } + + fn trade_mode(&self) -> TradeMode { + self.trade_mode + } + + fn creation_restriction_mode(&self) -> CreationRestrictionMode { + self.creation_restriction_mode + } + + fn data_contract_id(&self) -> Identifier { + self.data_contract_id + } + + fn requires_identity_encryption_bounded_key(&self) -> Option { + self.requires_identity_encryption_bounded_key + } + + fn requires_identity_decryption_bounded_key(&self) -> Option { + self.requires_identity_decryption_bounded_key + } + + fn security_level_requirement(&self) -> SecurityLevel { + self.security_level_requirement + } +} + +impl DocumentTypeV1Getters for DocumentTypeV1 { + fn document_creation_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.token_costs.document_creation_token_cost() + } + + fn document_replacement_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.token_costs.document_replacement_token_cost() + } + + fn document_deletion_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.token_costs.document_deletion_token_cost() + } + + fn document_transfer_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.token_costs.document_transfer_token_cost() + } + + fn document_price_update_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.token_costs.document_price_update_token_cost() + } + + fn document_purchase_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + self.token_costs.document_purchase_token_cost() + } +} diff --git a/packages/rs-dpp/src/data_contract/document_type/v1/mod.rs b/packages/rs-dpp/src/data_contract/document_type/v1/mod.rs new file mode 100644 index 00000000000..1ca0be47843 --- /dev/null +++ b/packages/rs-dpp/src/data_contract/document_type/v1/mod.rs @@ -0,0 +1,111 @@ +use indexmap::IndexMap; +use std::collections::{BTreeMap, BTreeSet}; + +use crate::data_contract::document_type::index::Index; +use crate::data_contract::document_type::index_level::IndexLevel; +use crate::data_contract::document_type::property::DocumentProperty; +use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; + +use crate::data_contract::document_type::methods::{ + DocumentTypeBasicMethods, DocumentTypeV0Methods, +}; +use crate::data_contract::document_type::restricted_creation::CreationRestrictionMode; +use crate::data_contract::document_type::token_costs::TokenCosts; +use crate::data_contract::document_type::v0::DocumentTypeV0; +use crate::document::transfer::Transferable; +use crate::identity::SecurityLevel; +use crate::nft::TradeMode; +use platform_value::{Identifier, Value}; + +#[cfg(feature = "validation")] +use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; +mod accessors; +#[cfg(feature = "random-document-types")] +pub mod random_document_type; + +#[derive(Debug, PartialEq, Clone)] +pub struct DocumentTypeV1 { + pub(in crate::data_contract) name: String, + pub(in crate::data_contract) schema: Value, + pub(in crate::data_contract) indices: BTreeMap, + pub(in crate::data_contract) index_structure: IndexLevel, + /// Flattened properties flatten all objects for quick lookups for indexes + /// Document field should not contain sub objects. + pub(in crate::data_contract) flattened_properties: IndexMap, + /// Document field can contain sub objects. + pub(in crate::data_contract) properties: IndexMap, + pub(in crate::data_contract) identifier_paths: BTreeSet, + pub(in crate::data_contract) binary_paths: BTreeSet, + /// The required fields on the document type + pub(in crate::data_contract) required_fields: BTreeSet, + /// The transient fields on the document type + pub(in crate::data_contract) transient_fields: BTreeSet, + /// Should documents keep history? + pub(in crate::data_contract) documents_keep_history: bool, + /// Are documents mutable? + pub(in crate::data_contract) documents_mutable: bool, + /// Can documents of this type be deleted? + pub(in crate::data_contract) documents_can_be_deleted: bool, + /// Can documents be transferred without a trade? + pub(in crate::data_contract) documents_transferable: Transferable, + /// How are these documents traded? + pub(in crate::data_contract) trade_mode: TradeMode, + /// Is document creation restricted? + pub(in crate::data_contract) creation_restriction_mode: CreationRestrictionMode, + /// The data contract id + pub(in crate::data_contract) data_contract_id: Identifier, + /// Encryption key storage requirements + pub(in crate::data_contract) requires_identity_encryption_bounded_key: + Option, + /// Decryption key storage requirements + pub(in crate::data_contract) requires_identity_decryption_bounded_key: + Option, + pub(in crate::data_contract) security_level_requirement: SecurityLevel, + #[cfg(feature = "validation")] + pub(in crate::data_contract) json_schema_validator: StatelessJsonSchemaLazyValidator, + /// The token costs associated with state transitions on this document type + pub(in crate::data_contract) token_costs: TokenCosts, +} + +impl DocumentTypeBasicMethods for DocumentTypeV1 {} + +impl DocumentTypeV0Methods for DocumentTypeV1 {} + +impl DocumentTypeV1 { + // Public method to set the data_contract_id + pub fn set_data_contract_id(&mut self, new_id: Identifier) { + self.data_contract_id = new_id; + } +} + +impl From for DocumentTypeV1 { + fn from(value: DocumentTypeV0) -> Self { + DocumentTypeV1 { + name: value.name, + schema: value.schema, + indices: value.indices, + index_structure: value.index_structure, + flattened_properties: value.flattened_properties, + properties: value.properties, + identifier_paths: value.identifier_paths, + binary_paths: value.binary_paths, + required_fields: value.required_fields, + transient_fields: value.transient_fields, + documents_keep_history: value.documents_keep_history, + documents_mutable: value.documents_mutable, + documents_can_be_deleted: value.documents_can_be_deleted, + documents_transferable: value.documents_transferable, + trade_mode: value.trade_mode, + creation_restriction_mode: value.creation_restriction_mode, + data_contract_id: value.data_contract_id, + requires_identity_encryption_bounded_key: value + .requires_identity_encryption_bounded_key, + requires_identity_decryption_bounded_key: value + .requires_identity_decryption_bounded_key, + security_level_requirement: value.security_level_requirement, + #[cfg(feature = "validation")] + json_schema_validator: value.json_schema_validator, + token_costs: TokenCosts::V0(Default::default()), + } + } +} diff --git a/packages/rs-dpp/src/data_contract/document_type/v1/random_document_type.rs b/packages/rs-dpp/src/data_contract/document_type/v1/random_document_type.rs new file mode 100644 index 00000000000..9cc7006956a --- /dev/null +++ b/packages/rs-dpp/src/data_contract/document_type/v1/random_document_type.rs @@ -0,0 +1,40 @@ +use crate::data_contract::document_type::v0::random_document_type::RandomDocumentTypeParameters; +use crate::data_contract::document_type::v0::DocumentTypeV0; +use crate::data_contract::document_type::v1::DocumentTypeV1; +use crate::version::PlatformVersion; +use crate::ProtocolError; +use platform_value::Identifier; +use rand::rngs::StdRng; + +impl DocumentTypeV1 { + pub fn random_document_type( + parameters: RandomDocumentTypeParameters, + data_contract_id: Identifier, + rng: &mut StdRng, + platform_version: &PlatformVersion, + ) -> Result { + Ok(DocumentTypeV0::random_document_type( + parameters, + data_contract_id, + rng, + platform_version, + )? + .into()) + } + + /// This is used to create an invalid random document type, often for testing + pub fn invalid_random_document_type( + parameters: RandomDocumentTypeParameters, + data_contract_id: Identifier, + rng: &mut StdRng, + platform_version: &PlatformVersion, + ) -> Result { + Ok(DocumentTypeV0::invalid_random_document_type( + parameters, + data_contract_id, + rng, + platform_version, + )? + .into()) + } +} diff --git a/packages/rs-dpp/src/data_contract/document_type/v0/validator.rs b/packages/rs-dpp/src/data_contract/document_type/validator.rs similarity index 100% rename from packages/rs-dpp/src/data_contract/document_type/v0/validator.rs rename to packages/rs-dpp/src/data_contract/document_type/validator.rs diff --git a/packages/rs-dpp/src/document/extended_document/v0/mod.rs b/packages/rs-dpp/src/document/extended_document/v0/mod.rs index b1ca400b982..bf944fb3b47 100644 --- a/packages/rs-dpp/src/document/extended_document/v0/mod.rs +++ b/packages/rs-dpp/src/document/extended_document/v0/mod.rs @@ -32,7 +32,9 @@ use std::collections::BTreeMap; use crate::data_contract::accessors::v0::DataContractV0Getters; use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; -use crate::data_contract::document_type::methods::DocumentTypeV0Methods; +use crate::data_contract::document_type::methods::{ + DocumentTypeBasicMethods, DocumentTypeV0Methods, +}; #[cfg(feature = "validation")] use crate::data_contract::validate_document::DataContractDocumentValidationMethodsV0; #[cfg(feature = "document-json-conversion")] diff --git a/packages/rs-dpp/src/document/v0/serialize.rs b/packages/rs-dpp/src/document/v0/serialize.rs index 0083216a12f..eb3d2a4ec0e 100644 --- a/packages/rs-dpp/src/document/v0/serialize.rs +++ b/packages/rs-dpp/src/document/v0/serialize.rs @@ -15,7 +15,7 @@ use crate::prelude::Revision; use crate::ProtocolError; use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; -use crate::data_contract::document_type::methods::DocumentTypeV0Methods; +use crate::data_contract::document_type::methods::DocumentTypeBasicMethods; use crate::document::serialization_traits::deserialize::v0::DocumentPlatformDeserializationMethodsV0; use crate::document::serialization_traits::serialize::v0::DocumentPlatformSerializationMethodsV0; use crate::document::serialization_traits::DocumentPlatformConversionMethodsV0; diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_create_transition/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_create_transition/v0/mod.rs index 7296c503821..14b5313cf1e 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_create_transition/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/document_create_transition/v0/mod.rs @@ -20,7 +20,7 @@ use crate::{document, errors::ProtocolError}; use crate::block::block_info::BlockInfo; use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; -use crate::data_contract::document_type::methods::DocumentTypeV0Methods; +use crate::data_contract::document_type::methods::DocumentTypeBasicMethods; use crate::data_contract::document_type::DocumentTypeRef; use crate::document::{Document, DocumentV0}; use crate::fee::Credits; diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs index 11de4e36561..afe15fccbb4 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs @@ -2,6 +2,7 @@ use crate::balances::credits::TokenAmount; #[cfg(feature = "state-transition-signing")] use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; +#[cfg(feature = "state-transition-signing")] use crate::data_contract::associated_token::token_distribution_key::TokenDistributionType; #[cfg(feature = "state-transition-signing")] use crate::data_contract::document_type::DocumentTypeRef; diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs index b1444757054..ee5a3f1919e 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs @@ -27,9 +27,9 @@ use crate::state_transition::batch_transition::methods::v0::DocumentsBatchTransi use std::iter::Map; use std::slice::Iter; -use crate::state_transition::batch_transition::{BatchTransitionV1, TokenClaimTransition}; +use crate::state_transition::batch_transition::BatchTransitionV1; #[cfg(feature = "state-transition-signing")] -use crate::state_transition::batch_transition::{TokenBurnTransition, TokenConfigUpdateTransition, TokenDestroyFrozenFundsTransition, TokenEmergencyActionTransition, TokenFreezeTransition, TokenMintTransition, TokenTransferTransition, TokenUnfreezeTransition}; +use crate::state_transition::batch_transition::{TokenClaimTransition, TokenBurnTransition, TokenConfigUpdateTransition, TokenDestroyFrozenFundsTransition, TokenEmergencyActionTransition, TokenFreezeTransition, TokenMintTransition, TokenTransferTransition, TokenUnfreezeTransition}; #[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::{ BatchTransition, DocumentDeleteTransition, @@ -45,6 +45,7 @@ use platform_version::version::{FeatureVersion, PlatformVersion}; use crate::balances::credits::TokenAmount; #[cfg(feature = "state-transition-signing")] use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; +#[cfg(feature = "state-transition-signing")] use crate::data_contract::associated_token::token_distribution_key::TokenDistributionType; #[cfg(feature = "state-transition-signing")] use crate::group::{GroupStateTransitionInfo, GroupStateTransitionInfoStatus}; @@ -64,6 +65,7 @@ use crate::state_transition::batch_transition::token_base_transition::v0::TokenB use crate::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; #[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_burn_transition::TokenBurnTransitionV0; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_claim_transition::TokenClaimTransitionV0; #[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; diff --git a/packages/rs-drive-abci/tests/supporting_files/contract/basic-token-with-document/basic-token-with-document.json b/packages/rs-drive-abci/tests/supporting_files/contract/basic-token-with-document/basic-token-with-document.json new file mode 100644 index 00000000000..b6442f453c5 --- /dev/null +++ b/packages/rs-drive-abci/tests/supporting_files/contract/basic-token-with-document/basic-token-with-document.json @@ -0,0 +1,158 @@ +{ + "$format_version": "1", + "id": "EbL1zYg1JrpPX9rYbASihRsSEgwKbqZAJu6B1Z2SKKU2", + "ownerId": "F1Ue2M5PfDjDX69NqrZdChdEbwF2SYZ8UF4qNjsCQu1d", + "version": 1, + "documentSchemas": { + "card": { + "type": "object", + "documentsMutable": false, + "canBeDeleted": false, + "transferable": 1, + "tokenCost": { + "tokenPosition": 0, + "create": 10, + "replace": 1, + "delete": 1 + }, + "properties": { + "name": { + "type": "string", + "description": "Name of the card", + "maxLength": 63, + "position": 0 + }, + "description": { + "type": "string", + "description": "Description of the card", + "maxLength": 256, + "position": 1 + }, + "imageUrl": { + "type": "string", + "description": "URL of the image associated with the card", + "maxLength": 2048, + "format": "uri", + "position": 2 + }, + "imageHash": { + "type": "array", + "description": "SHA256 hash of the bytes of the image specified by imageUrl", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "position": 3 + }, + "imageFingerprint": { + "type": "array", + "description": "dHash of the image specified by imageUrl", + "byteArray": true, + "minItems": 8, + "maxItems": 8, + "position": 4 + }, + "attack": { + "type": "integer", + "description": "Attack power of the card", + "minimum": 0, + "position": 5 + }, + "defense": { + "type": "integer", + "description": "Defense level of the card", + "minimum": 0, + "position": 6 + } + }, + "indices": [ + { + "name": "owner", + "properties": [ + { + "$ownerId": "asc" + } + ] + }, + { + "name": "attack", + "properties": [ + { + "attack": "asc" + } + ] + }, + { + "name": "defense", + "properties": [ + { + "defense": "asc" + } + ] + }, + { + "name": "transferredAt", + "properties": [ + { + "$transferredAt": "asc" + } + ] + }, + { + "name": "ownerTransferredAt", + "properties": [ + { + "$ownerId": "asc" + }, + { + "$transferredAt": "asc" + } + ] + }, + { + "name": "transferredAtBlockHeight", + "properties": [ + { + "$transferredAtBlockHeight": "asc" + } + ] + }, + { + "name": "transferredAtCoreBlockHeight", + "properties": [ + { + "$transferredAtCoreBlockHeight": "asc" + } + ] + } + ], + "required": [ + "name", + "$transferredAt", + "$transferredAtBlockHeight", + "$transferredAtCoreBlockHeight", + "attack", + "defense" + ], + "additionalProperties": false + } + }, + "tokens": { + "0": { + "$format_version": "0", + "conventions": { + "$format_version": "0", + "localizations": { + "en": { + "$format_version": "0", + "shouldCapitalize": false, + "pluralForm": "tests", + "singularForm": "test" + } + }, + "decimals": 8 + }, + "baseSupply": 100000, + "maxSupply": null + } + } +} diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/mod.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/mod.rs index 635e315e86e..ff5987cdbe0 100644 --- a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/mod.rs +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/mod.rs @@ -12,7 +12,7 @@ use dpp::data_contract::config::v0::DataContractConfigGettersV0; use dpp::data_contract::DataContract; use dpp::fee::fee_result::FeeResult; -use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; +use dpp::data_contract::document_type::methods::DocumentTypeBasicMethods; use dpp::serialization::PlatformSerializableWithPlatformVersion; use crate::drive::votes::paths::{ diff --git a/packages/rs-drive/src/drive/contract/update/update_contract/v0/mod.rs b/packages/rs-drive/src/drive/contract/update/update_contract/v0/mod.rs index 141749a2ffb..84a6c4aed45 100644 --- a/packages/rs-drive/src/drive/contract/update/update_contract/v0/mod.rs +++ b/packages/rs-drive/src/drive/contract/update/update_contract/v0/mod.rs @@ -13,7 +13,7 @@ use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contract::DataContract; use dpp::fee::fee_result::FeeResult; -use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; +use dpp::data_contract::document_type::methods::{DocumentTypeBasicMethods, DocumentTypeV0Methods}; use dpp::serialization::PlatformSerializableWithPlatformVersion; use dpp::fee::default_costs::CachedEpochIndexFeeVersions; diff --git a/packages/rs-drive/src/drive/document/insert/add_document_to_primary_storage/v0/mod.rs b/packages/rs-drive/src/drive/document/insert/add_document_to_primary_storage/v0/mod.rs index ea1ae0f2a7c..68404605e7f 100644 --- a/packages/rs-drive/src/drive/document/insert/add_document_to_primary_storage/v0/mod.rs +++ b/packages/rs-drive/src/drive/document/insert/add_document_to_primary_storage/v0/mod.rs @@ -34,7 +34,7 @@ use dpp::block::block_info::BlockInfo; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::config::v0::DataContractConfigGettersV0; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; -use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; +use dpp::data_contract::document_type::methods::{DocumentTypeBasicMethods, DocumentTypeV0Methods}; use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0; use dpp::document::DocumentV0Getters; diff --git a/packages/rs-drive/src/drive/document/insert/add_reference_for_index_level_for_contract_operations/v0/mod.rs b/packages/rs-drive/src/drive/document/insert/add_reference_for_index_level_for_contract_operations/v0/mod.rs index c3744cbcdcb..a65c3c9e110 100644 --- a/packages/rs-drive/src/drive/document/insert/add_reference_for_index_level_for_contract_operations/v0/mod.rs +++ b/packages/rs-drive/src/drive/document/insert/add_reference_for_index_level_for_contract_operations/v0/mod.rs @@ -15,7 +15,7 @@ use crate::util::object_size_info::KeyElementInfo::{KeyElement, KeyUnknownElemen use crate::util::object_size_info::{DocumentAndContractInfo, PathInfo, PathKeyElementInfo}; use crate::util::storage_flags::StorageFlags; use crate::util::type_constants::DEFAULT_HASH_SIZE_U8; -use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; +use dpp::data_contract::document_type::methods::DocumentTypeBasicMethods; use dpp::data_contract::document_type::IndexLevelTypeInfo; use dpp::document::DocumentV0Getters; use dpp::version::drive_versions::DriveVersion; diff --git a/packages/rs-drive/src/drive/document/insert_contested/add_contested_document_to_primary_storage/v0/mod.rs b/packages/rs-drive/src/drive/document/insert_contested/add_contested_document_to_primary_storage/v0/mod.rs index 5a18464be37..eefafcd0325 100644 --- a/packages/rs-drive/src/drive/document/insert_contested/add_contested_document_to_primary_storage/v0/mod.rs +++ b/packages/rs-drive/src/drive/document/insert_contested/add_contested_document_to_primary_storage/v0/mod.rs @@ -26,7 +26,7 @@ use crate::util::grove_operations::QueryTarget::QueryTargetValue; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; -use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; +use dpp::data_contract::document_type::methods::{DocumentTypeBasicMethods, DocumentTypeV0Methods}; use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0; use dpp::document::DocumentV0Getters; diff --git a/packages/rs-drive/src/drive/document/insert_contested/add_contested_reference_and_vote_subtree_to_document_operations/v0/mod.rs b/packages/rs-drive/src/drive/document/insert_contested/add_contested_reference_and_vote_subtree_to_document_operations/v0/mod.rs index cd4fa6ecd11..52303f9cf01 100644 --- a/packages/rs-drive/src/drive/document/insert_contested/add_contested_reference_and_vote_subtree_to_document_operations/v0/mod.rs +++ b/packages/rs-drive/src/drive/document/insert_contested/add_contested_reference_and_vote_subtree_to_document_operations/v0/mod.rs @@ -14,7 +14,7 @@ use crate::util::object_size_info::KeyElementInfo::{KeyElement, KeyUnknownElemen use crate::util::object_size_info::{DocumentAndContractInfo, PathInfo, PathKeyElementInfo}; use crate::util::storage_flags::StorageFlags; use crate::util::type_constants::{DEFAULT_HASH_SIZE_U8, U8_SIZE_U32, U8_SIZE_U8}; -use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; +use dpp::data_contract::document_type::methods::DocumentTypeBasicMethods; use dpp::version::drive_versions::DriveVersion; use grovedb::batch::key_info::KeyInfo; use grovedb::batch::KeyInfoPath; diff --git a/packages/rs-drive/src/drive/document/paths.rs b/packages/rs-drive/src/drive/document/paths.rs index 9fa8b56772f..9d1c0516d97 100644 --- a/packages/rs-drive/src/drive/document/paths.rs +++ b/packages/rs-drive/src/drive/document/paths.rs @@ -1,7 +1,7 @@ use crate::drive::{constants, RootTree}; use crate::util::type_constants::DEFAULT_HASH_SIZE_U8; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; -use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; +use dpp::data_contract::document_type::methods::DocumentTypeBasicMethods; use dpp::data_contract::document_type::DocumentTypeRef; #[cfg(feature = "server")] use grovedb::batch::key_info::KeyInfo; diff --git a/packages/rs-drive/src/drive/document/update/internal/update_document_for_contract_operations/v0/mod.rs b/packages/rs-drive/src/drive/document/update/internal/update_document_for_contract_operations/v0/mod.rs index 5a6a14e759e..a3c0aec24c5 100644 --- a/packages/rs-drive/src/drive/document/update/internal/update_document_for_contract_operations/v0/mod.rs +++ b/packages/rs-drive/src/drive/document/update/internal/update_document_for_contract_operations/v0/mod.rs @@ -29,7 +29,7 @@ use crate::drive::document::paths::{ contract_documents_keeping_history_primary_key_path_for_document_id, contract_documents_primary_key_path, }; -use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; +use dpp::data_contract::document_type::methods::DocumentTypeBasicMethods; use dpp::version::PlatformVersion; use grovedb::batch::key_info::KeyInfo; use grovedb::batch::key_info::KeyInfo::KnownKey; diff --git a/packages/rs-drive/src/drive/tokens/distribution/add_pre_programmed_distribution/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/add_pre_programmed_distribution/v0/mod.rs index 9ff30fe2798..6a80686e9bc 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/add_pre_programmed_distribution/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/add_pre_programmed_distribution/v0/mod.rs @@ -2,7 +2,6 @@ use crate::drive::tokens::paths::{ token_ms_timed_at_time_distributions_path_vec, token_ms_timed_distributions_path_vec, token_pre_programmed_at_time_distribution_path_vec, token_pre_programmed_distributions_path, token_root_pre_programmed_distributions_path, - TOKEN_PERPETUAL_DISTRIBUTIONS_FOR_IDENTITIES_LAST_CLAIM_KEY, TOKEN_PRE_PROGRAMMED_DISTRIBUTIONS_FOR_IDENTITIES_LAST_CLAIM_KEY, TOKEN_PRE_PROGRAMMED_DISTRIBUTIONS_KEY, }; diff --git a/packages/rs-drive/src/drive/tokens/distribution/mark_perpetual_release_as_distributed/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/mark_perpetual_release_as_distributed/v0/mod.rs index 7cde1c102b6..2bacadcea45 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/mark_perpetual_release_as_distributed/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/mark_perpetual_release_as_distributed/v0/mod.rs @@ -1,4 +1,4 @@ -use crate::drive::tokens::paths::{token_perpetual_distributions_identity_last_claimed_by_identity_path_vec, token_perpetual_distributions_identity_last_claimed_time_path_vec, TOKEN_PERPETUAL_DISTRIBUTIONS_FOR_IDENTITIES_LAST_CLAIM_KEY}; +use crate::drive::tokens::paths::token_perpetual_distributions_identity_last_claimed_time_path_vec; use crate::drive::Drive; use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; diff --git a/packages/rs-drive/src/drive/tokens/distribution/mark_pre_programmed_release_as_distributed/v0/mod.rs b/packages/rs-drive/src/drive/tokens/distribution/mark_pre_programmed_release_as_distributed/v0/mod.rs index fc481553d7e..4fb62e78f15 100644 --- a/packages/rs-drive/src/drive/tokens/distribution/mark_pre_programmed_release_as_distributed/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/distribution/mark_pre_programmed_release_as_distributed/v0/mod.rs @@ -1,6 +1,5 @@ use crate::drive::tokens::paths::{ token_distributions_root_path_vec, token_ms_timed_at_time_distributions_path_vec, - token_pre_programmed_distributions_identity_last_claimed_time_path, token_pre_programmed_distributions_identity_last_claimed_time_path_vec, TOKEN_PRE_PROGRAMMED_DISTRIBUTIONS_KEY, }; diff --git a/packages/rs-drive/src/drive/tokens/estimated_costs/for_token_perpetual_distribution/v0/mod.rs b/packages/rs-drive/src/drive/tokens/estimated_costs/for_token_perpetual_distribution/v0/mod.rs index 45c6380a279..bee40e512db 100644 --- a/packages/rs-drive/src/drive/tokens/estimated_costs/for_token_perpetual_distribution/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/estimated_costs/for_token_perpetual_distribution/v0/mod.rs @@ -1,13 +1,12 @@ use crate::drive::tokens::paths::{ - token_distributions_root_path_vec, - token_perpetual_distributions_identity_last_claimed_time_path_vec, - token_perpetual_distributions_path_vec, token_root_perpetual_distributions_path_vec, + token_distributions_root_path_vec, token_perpetual_distributions_path_vec, + token_root_perpetual_distributions_path_vec, }; use crate::drive::Drive; -use crate::util::type_constants::{DEFAULT_HASH_SIZE_U8, U64_SIZE_U32, U8_SIZE_U8}; +use crate::util::type_constants::{DEFAULT_HASH_SIZE_U8, U8_SIZE_U8}; use grovedb::batch::KeyInfoPath; -use grovedb::EstimatedLayerCount::{ApproximateElements, EstimatedLevel}; -use grovedb::EstimatedLayerSizes::{AllItems, AllSubtrees}; +use grovedb::EstimatedLayerCount::EstimatedLevel; +use grovedb::EstimatedLayerSizes::AllSubtrees; use grovedb::EstimatedSumTrees::NoSumTrees; use grovedb::{EstimatedLayerInformation, TreeType}; use std::collections::HashMap; diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_create_transition_action/v0/mod.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_create_transition_action/v0/mod.rs index e30b3948a70..5e69c949605 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_create_transition_action/v0/mod.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_create_transition_action/v0/mod.rs @@ -10,7 +10,7 @@ use dpp::ProtocolError; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; -use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; +use dpp::data_contract::document_type::methods::DocumentTypeBasicMethods; use dpp::document::property_names::{ CREATED_AT, CREATED_AT_BLOCK_HEIGHT, CREATED_AT_CORE_BLOCK_HEIGHT, TRANSFERRED_AT, TRANSFERRED_AT_BLOCK_HEIGHT, TRANSFERRED_AT_CORE_BLOCK_HEIGHT, UPDATED_AT, diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_claim_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_claim_transition_action/v0/transformer.rs index 17f672757e1..3467688a8df 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_claim_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_claim_transition_action/v0/transformer.rs @@ -1,5 +1,4 @@ use std::collections::BTreeMap; -use std::process::id; use std::sync::Arc; use grovedb::TransactionArg; use dpp::balances::credits::TokenAmount; @@ -246,20 +245,19 @@ impl TokenClaimTransitionActionV0 { fee_result.checked_add_assign(last_paid_time_fee_result)?; - let mut distributions_in_past_for_owner: BTreeMap = - times - .iter() - .filter_map(|(timestamp, distribution)| { - if timestamp > &block_info.time_ms { - // Don't get the ones in the future - None - } else { - distribution - .get(&owner_id) - .map(|amount| (*timestamp, *amount)) - } - }) - .collect(); + let distributions_in_past_for_owner: BTreeMap = times + .iter() + .filter_map(|(timestamp, distribution)| { + if timestamp > &block_info.time_ms { + // Don't get the ones in the future + None + } else { + distribution + .get(&owner_id) + .map(|amount| (*timestamp, *amount)) + } + }) + .collect(); let distribution_after_last_paid: Option<(TimestampMillis, TokenAmount)> = if let Some(last_paid) = last_paid_moment { diff --git a/packages/rs-drive/src/util/batch/drive_op_batch/token.rs b/packages/rs-drive/src/util/batch/drive_op_batch/token.rs index d67500a16a4..db3cead1f91 100644 --- a/packages/rs-drive/src/util/batch/drive_op_batch/token.rs +++ b/packages/rs-drive/src/util/batch/drive_op_batch/token.rs @@ -12,7 +12,6 @@ use grovedb::batch::KeyInfoPath; use grovedb::{EstimatedLayerInformation, TransactionArg}; use platform_version::version::PlatformVersion; use std::collections::HashMap; -use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_recipient::TokenDistributionRecipient; use dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment; /// Operations on Tokens diff --git a/packages/rs-drive/src/util/object_size_info/document_info.rs b/packages/rs-drive/src/util/object_size_info/document_info.rs index e671b293a88..fc9f5adf3b8 100644 --- a/packages/rs-drive/src/util/object_size_info/document_info.rs +++ b/packages/rs-drive/src/util/object_size_info/document_info.rs @@ -10,7 +10,7 @@ use crate::util::type_constants::{ U64_SIZE_U8, }; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; -use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; +use dpp::data_contract::document_type::methods::DocumentTypeBasicMethods; use dpp::data_contract::document_type::{DocumentTypeRef, IndexLevel}; use dpp::document::document_methods::DocumentMethodsV0; use dpp::document::{Document, DocumentV0Getters}; From 4dfcaf4cbdf03784658c72bf16e57a8697ed88d3 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sat, 15 Mar 2025 05:04:07 +0700 Subject: [PATCH 2/7] more work --- .../document_type/accessors/mod.rs | 244 +++++- .../document_type/accessors/v0/mod.rs | 10 + .../document_type/accessors/v1/mod.rs | 2 +- .../class_methods/try_from_schema/mod.rs | 222 ++++- .../class_methods/try_from_schema/v0/mod.rs | 227 +---- .../class_methods/try_from_schema/v1/mod.rs | 791 ++++++++++++++++++ .../methods/versioned_methods.rs | 4 +- .../src/data_contract/document_type/mod.rs | 10 + .../document_type/token_costs/mod.rs | 5 +- .../document_type/v0/accessors.rs | 16 +- .../document_type/v1/accessors.rs | 16 +- .../methods/validate_document/v0/mod.rs | 6 +- .../methods/validate_update/v0/mod.rs | 5 +- .../src/data_contract/v0/accessors/mod.rs | 8 +- .../src/data_contract/v1/accessors/mod.rs | 8 +- .../src/document/extended_document/v0/mod.rs | 4 +- .../document_base_transaction_action/mod.rs | 62 ++ .../state_v0/mod.rs | 73 ++ .../state_v1/mod.rs | 14 + .../state_v0/mod.rs | 16 +- .../state_v0/mod.rs | 18 +- .../state_v0/mod.rs | 17 +- .../state_v0/mod.rs | 17 +- .../state_v0/mod.rs | 17 +- .../batch/action_validation/document/mod.rs | 1 + .../triggers/withdrawals/v0/mod.rs | 2 + .../batch/tests/document/creation.rs | 97 +++ .../batch/tests/token/mod.rs | 2 - .../state_transition/state_transitions/mod.rs | 62 ++ .../src/query/document_query/v0/mod.rs | 1 - .../basic-token-with-document.json | 7 +- .../crypto-card-game-in-game-currency.json | 45 +- .../contract/update/update_contract/v0/mod.rs | 2 +- .../for_token_total_supply/v0/mod.rs | 2 +- .../remove_from_token_total_supply/v0/mod.rs | 54 +- .../document/document_create_transition.rs | 14 +- .../document/document_delete_transition.rs | 20 +- .../document/document_purchase_transition.rs | 20 +- .../document/document_replace_transition.rs | 18 +- .../document/document_transfer_transition.rs | 18 +- .../document_update_price_transition.rs | 18 +- .../document_base_transition_action/mod.rs | 7 + .../transformer.rs | 9 +- .../document_base_transition_action/v0/mod.rs | 9 +- .../v0/transformer.rs | 38 +- .../v0/transformer.rs | 4 +- .../v0/transformer.rs | 6 +- .../v0/transformer.rs | 3 +- .../v0/transformer.rs | 3 +- .../v0/transformer.rs | 3 +- .../v0/transformer.rs | 3 +- .../v0/mod.rs | 8 +- .../dpp_versions/dpp_contract_versions/v2.rs | 2 +- .../drive_abci_validation_versions/mod.rs | 1 + .../drive_abci_validation_versions/v1.rs | 1 + .../drive_abci_validation_versions/v2.rs | 1 + .../drive_abci_validation_versions/v3.rs | 1 + .../drive_abci_validation_versions/v4.rs | 1 + .../drive_abci_validation_versions/v5.rs | 1 + .../transition/update_price_of_document.rs | 1 - 60 files changed, 1980 insertions(+), 317 deletions(-) create mode 100644 packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v1/mod.rs create mode 100644 packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_base_transaction_action/mod.rs create mode 100644 packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_base_transaction_action/state_v0/mod.rs diff --git a/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs b/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs index 6fa3807633c..02ed549858f 100644 --- a/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs @@ -8,8 +8,11 @@ use crate::data_contract::document_type::{DocumentType, DocumentTypeMutRef, Docu use platform_value::{Identifier, Value}; +use crate::balances::credits::TokenAmount; use crate::data_contract::document_type::restricted_creation::CreationRestrictionMode; +use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; +use crate::data_contract::TokenContractPosition; use crate::document::transfer::Transferable; use crate::identity::SecurityLevel; use crate::nft::TradeMode; @@ -22,132 +25,171 @@ impl DocumentTypeV0Getters for DocumentType { fn name(&self) -> &String { match self { DocumentType::V0(v0) => v0.name(), + DocumentType::V1(v1) => v1.name(), } } fn schema(&self) -> &Value { match self { DocumentType::V0(v0) => v0.schema(), + DocumentType::V1(v1) => v1.schema(), } } fn schema_owned(self) -> Value { match self { DocumentType::V0(v0) => v0.schema_owned(), + DocumentType::V1(v1) => v1.schema_owned(), } } fn indexes(&self) -> &BTreeMap { match self { DocumentType::V0(v0) => v0.indexes(), + DocumentType::V1(v1) => v1.indexes(), } } fn index_structure(&self) -> &IndexLevel { match self { DocumentType::V0(v0) => v0.index_structure(), + DocumentType::V1(v1) => v1.index_structure(), } } fn flattened_properties(&self) -> &IndexMap { match self { DocumentType::V0(v0) => v0.flattened_properties(), + DocumentType::V1(v1) => v1.flattened_properties(), } } fn properties(&self) -> &IndexMap { match self { DocumentType::V0(v0) => v0.properties(), + DocumentType::V1(v1) => v1.properties(), } } fn identifier_paths(&self) -> &BTreeSet { match self { DocumentType::V0(v0) => v0.identifier_paths(), + DocumentType::V1(v1) => v1.identifier_paths(), } } fn binary_paths(&self) -> &BTreeSet { match self { DocumentType::V0(v0) => v0.binary_paths(), + DocumentType::V1(v1) => v1.binary_paths(), } } fn required_fields(&self) -> &BTreeSet { match self { DocumentType::V0(v0) => v0.required_fields(), + DocumentType::V1(v1) => v1.required_fields(), } } fn transient_fields(&self) -> &BTreeSet { match self { DocumentType::V0(v0) => v0.transient_fields(), + DocumentType::V1(v1) => v1.transient_fields(), } } fn documents_keep_history(&self) -> bool { match self { DocumentType::V0(v0) => v0.documents_keep_history(), + DocumentType::V1(v1) => v1.documents_keep_history(), } } fn documents_mutable(&self) -> bool { match self { DocumentType::V0(v0) => v0.documents_mutable(), + DocumentType::V1(v1) => v1.documents_mutable(), } } fn documents_can_be_deleted(&self) -> bool { match self { DocumentType::V0(v0) => v0.documents_can_be_deleted(), + DocumentType::V1(v1) => v1.documents_can_be_deleted(), } } fn trade_mode(&self) -> TradeMode { match self { DocumentType::V0(v0) => v0.trade_mode(), + DocumentType::V1(v1) => v1.trade_mode(), } } fn creation_restriction_mode(&self) -> CreationRestrictionMode { match self { DocumentType::V0(v0) => v0.creation_restriction_mode(), + DocumentType::V1(v1) => v1.creation_restriction_mode(), } } fn documents_transferable(&self) -> Transferable { match self { DocumentType::V0(v0) => v0.documents_transferable(), + DocumentType::V1(v1) => v1.documents_transferable(), } } fn data_contract_id(&self) -> Identifier { match self { DocumentType::V0(v0) => v0.data_contract_id(), + DocumentType::V1(v1) => v1.data_contract_id(), } } fn requires_identity_encryption_bounded_key(&self) -> Option { match self { DocumentType::V0(v0) => v0.requires_identity_encryption_bounded_key(), + DocumentType::V1(v1) => v1.requires_identity_encryption_bounded_key(), } } fn requires_identity_decryption_bounded_key(&self) -> Option { match self { DocumentType::V0(v0) => v0.requires_identity_decryption_bounded_key(), + DocumentType::V1(v1) => v1.requires_identity_decryption_bounded_key(), } } fn security_level_requirement(&self) -> SecurityLevel { match self { DocumentType::V0(v0) => v0.security_level_requirement(), + DocumentType::V1(v1) => v1.security_level_requirement(), } } fn find_contested_index(&self) -> Option<&Index> { match self { DocumentType::V0(v0) => v0.find_contested_index(), + DocumentType::V1(v1) => v1.find_contested_index(), + } + } + + #[cfg(feature = "validation")] + fn json_schema_validator_ref(&self) -> &StatelessJsonSchemaLazyValidator { + match self { + DocumentType::V0(v0) => v0.json_schema_validator_ref(), + DocumentType::V1(v1) => v1.json_schema_validator_ref(), + } + } +} + +impl DocumentTypeV0Setters for DocumentType { + fn set_data_contract_id(&mut self, data_contract_id: Identifier) { + match self { + DocumentType::V0(v0) => v0.set_data_contract_id(data_contract_id), + DocumentType::V1(v1) => v1.set_data_contract_id(data_contract_id), } } } @@ -156,266 +198,466 @@ impl<'a> DocumentTypeV0Getters for DocumentTypeRef<'a> { fn name(&self) -> &String { match self { DocumentTypeRef::V0(v0) => v0.name(), + DocumentTypeRef::V1(v1) => v1.name(), } } fn schema(&self) -> &Value { match self { DocumentTypeRef::V0(v0) => v0.schema(), + DocumentTypeRef::V1(v1) => v1.schema(), } } fn schema_owned(self) -> Value { match self { DocumentTypeRef::V0(v0) => v0.clone().schema_owned(), + DocumentTypeRef::V1(v1) => v1.clone().schema_owned(), } } fn indexes(&self) -> &BTreeMap { match self { DocumentTypeRef::V0(v0) => v0.indexes(), + DocumentTypeRef::V1(v1) => v1.indexes(), } } fn index_structure(&self) -> &IndexLevel { match self { DocumentTypeRef::V0(v0) => v0.index_structure(), + DocumentTypeRef::V1(v1) => v1.index_structure(), } } fn flattened_properties(&self) -> &IndexMap { match self { DocumentTypeRef::V0(v0) => v0.flattened_properties(), + DocumentTypeRef::V1(v1) => v1.flattened_properties(), } } fn properties(&self) -> &IndexMap { match self { DocumentTypeRef::V0(v0) => v0.properties(), + DocumentTypeRef::V1(v1) => v1.properties(), } } fn identifier_paths(&self) -> &BTreeSet { match self { DocumentTypeRef::V0(v0) => v0.identifier_paths(), + DocumentTypeRef::V1(v1) => v1.identifier_paths(), } } fn binary_paths(&self) -> &BTreeSet { match self { DocumentTypeRef::V0(v0) => v0.binary_paths(), + DocumentTypeRef::V1(v1) => v1.binary_paths(), } } fn required_fields(&self) -> &BTreeSet { match self { DocumentTypeRef::V0(v0) => v0.required_fields(), + DocumentTypeRef::V1(v1) => v1.required_fields(), } } fn transient_fields(&self) -> &BTreeSet { match self { DocumentTypeRef::V0(v0) => v0.transient_fields(), + DocumentTypeRef::V1(v1) => v1.transient_fields(), } } fn documents_keep_history(&self) -> bool { match self { DocumentTypeRef::V0(v0) => v0.documents_keep_history(), + DocumentTypeRef::V1(v1) => v1.documents_keep_history(), } } fn documents_mutable(&self) -> bool { match self { DocumentTypeRef::V0(v0) => v0.documents_mutable(), + DocumentTypeRef::V1(v1) => v1.documents_mutable(), } } fn documents_can_be_deleted(&self) -> bool { match self { DocumentTypeRef::V0(v0) => v0.documents_can_be_deleted(), + DocumentTypeRef::V1(v1) => v1.documents_can_be_deleted(), } } fn documents_transferable(&self) -> Transferable { match self { DocumentTypeRef::V0(v0) => v0.documents_transferable(), + DocumentTypeRef::V1(v1) => v1.documents_transferable(), } } fn trade_mode(&self) -> TradeMode { match self { DocumentTypeRef::V0(v0) => v0.trade_mode(), + DocumentTypeRef::V1(v1) => v1.trade_mode(), } } fn creation_restriction_mode(&self) -> CreationRestrictionMode { match self { DocumentTypeRef::V0(v0) => v0.creation_restriction_mode(), + DocumentTypeRef::V1(v1) => v1.creation_restriction_mode(), } } fn data_contract_id(&self) -> Identifier { match self { DocumentTypeRef::V0(v0) => v0.data_contract_id(), + DocumentTypeRef::V1(v1) => v1.data_contract_id(), } } fn requires_identity_encryption_bounded_key(&self) -> Option { match self { DocumentTypeRef::V0(v0) => v0.requires_identity_encryption_bounded_key(), + DocumentTypeRef::V1(v1) => v1.requires_identity_encryption_bounded_key(), } } fn requires_identity_decryption_bounded_key(&self) -> Option { match self { DocumentTypeRef::V0(v0) => v0.requires_identity_decryption_bounded_key(), + DocumentTypeRef::V1(v1) => v1.requires_identity_decryption_bounded_key(), } } fn security_level_requirement(&self) -> SecurityLevel { match self { DocumentTypeRef::V0(v0) => v0.security_level_requirement(), + DocumentTypeRef::V1(v1) => v1.security_level_requirement(), } } fn find_contested_index(&self) -> Option<&Index> { match self { DocumentTypeRef::V0(v0) => v0.find_contested_index(), + DocumentTypeRef::V1(v1) => v1.find_contested_index(), } } -} + #[cfg(feature = "validation")] + fn json_schema_validator_ref(&self) -> &StatelessJsonSchemaLazyValidator { + match self { + DocumentTypeRef::V0(v0) => v0.json_schema_validator_ref(), + DocumentTypeRef::V1(v1) => v1.json_schema_validator_ref(), + } + } +} impl<'a> DocumentTypeV0Getters for DocumentTypeMutRef<'a> { fn name(&self) -> &String { match self { DocumentTypeMutRef::V0(v0) => v0.name(), + DocumentTypeMutRef::V1(v1) => v1.name(), } } fn schema(&self) -> &Value { match self { DocumentTypeMutRef::V0(v0) => v0.schema(), + DocumentTypeMutRef::V1(v1) => v1.schema(), } } fn schema_owned(self) -> Value { match self { DocumentTypeMutRef::V0(v0) => v0.clone().schema_owned(), + DocumentTypeMutRef::V1(v1) => v1.clone().schema_owned(), } } fn indexes(&self) -> &BTreeMap { match self { DocumentTypeMutRef::V0(v0) => v0.indexes(), + DocumentTypeMutRef::V1(v1) => v1.indexes(), } } fn index_structure(&self) -> &IndexLevel { match self { DocumentTypeMutRef::V0(v0) => v0.index_structure(), + DocumentTypeMutRef::V1(v1) => v1.index_structure(), } } fn flattened_properties(&self) -> &IndexMap { match self { DocumentTypeMutRef::V0(v0) => v0.flattened_properties(), + DocumentTypeMutRef::V1(v1) => v1.flattened_properties(), } } fn properties(&self) -> &IndexMap { match self { DocumentTypeMutRef::V0(v0) => v0.properties(), + DocumentTypeMutRef::V1(v1) => v1.properties(), } } fn identifier_paths(&self) -> &BTreeSet { match self { DocumentTypeMutRef::V0(v0) => v0.identifier_paths(), + DocumentTypeMutRef::V1(v1) => v1.identifier_paths(), } } fn binary_paths(&self) -> &BTreeSet { match self { DocumentTypeMutRef::V0(v0) => v0.binary_paths(), + DocumentTypeMutRef::V1(v1) => v1.binary_paths(), } } fn required_fields(&self) -> &BTreeSet { match self { DocumentTypeMutRef::V0(v0) => v0.required_fields(), + DocumentTypeMutRef::V1(v1) => v1.required_fields(), } } fn transient_fields(&self) -> &BTreeSet { match self { DocumentTypeMutRef::V0(v0) => v0.transient_fields(), + DocumentTypeMutRef::V1(v1) => v1.transient_fields(), } } fn documents_keep_history(&self) -> bool { match self { DocumentTypeMutRef::V0(v0) => v0.documents_keep_history(), + DocumentTypeMutRef::V1(v1) => v1.documents_keep_history(), } } fn documents_mutable(&self) -> bool { match self { DocumentTypeMutRef::V0(v0) => v0.documents_mutable(), + DocumentTypeMutRef::V1(v1) => v1.documents_mutable(), } } fn documents_can_be_deleted(&self) -> bool { match self { DocumentTypeMutRef::V0(v0) => v0.documents_can_be_deleted(), + DocumentTypeMutRef::V1(v1) => v1.documents_can_be_deleted(), } } fn documents_transferable(&self) -> Transferable { match self { DocumentTypeMutRef::V0(v0) => v0.documents_transferable(), + DocumentTypeMutRef::V1(v1) => v1.documents_transferable(), } } fn trade_mode(&self) -> TradeMode { match self { DocumentTypeMutRef::V0(v0) => v0.trade_mode(), + DocumentTypeMutRef::V1(v1) => v1.trade_mode(), } } fn creation_restriction_mode(&self) -> CreationRestrictionMode { match self { DocumentTypeMutRef::V0(v0) => v0.creation_restriction_mode(), + DocumentTypeMutRef::V1(v1) => v1.creation_restriction_mode(), } } fn data_contract_id(&self) -> Identifier { match self { DocumentTypeMutRef::V0(v0) => v0.data_contract_id(), + DocumentTypeMutRef::V1(v1) => v1.data_contract_id(), } } fn requires_identity_encryption_bounded_key(&self) -> Option { match self { DocumentTypeMutRef::V0(v0) => v0.requires_identity_encryption_bounded_key(), + DocumentTypeMutRef::V1(v1) => v1.requires_identity_encryption_bounded_key(), } } fn requires_identity_decryption_bounded_key(&self) -> Option { match self { DocumentTypeMutRef::V0(v0) => v0.requires_identity_decryption_bounded_key(), + DocumentTypeMutRef::V1(v1) => v1.requires_identity_decryption_bounded_key(), } } fn security_level_requirement(&self) -> SecurityLevel { match self { DocumentTypeMutRef::V0(v0) => v0.security_level_requirement(), + DocumentTypeMutRef::V1(v1) => v1.security_level_requirement(), } } fn find_contested_index(&self) -> Option<&Index> { match self { DocumentTypeMutRef::V0(v0) => v0.find_contested_index(), + DocumentTypeMutRef::V1(v1) => v1.find_contested_index(), + } + } + + #[cfg(feature = "validation")] + fn json_schema_validator_ref(&self) -> &StatelessJsonSchemaLazyValidator { + match self { + DocumentTypeMutRef::V0(v0) => v0.json_schema_validator_ref(), + DocumentTypeMutRef::V1(v1) => v1.json_schema_validator_ref(), + } + } +} + +impl<'a> DocumentTypeV0Setters for DocumentTypeMutRef<'a> { + fn set_data_contract_id(&mut self, data_contract_id: Identifier) { + match self { + DocumentTypeMutRef::V0(v0) => v0.set_data_contract_id(data_contract_id), + DocumentTypeMutRef::V1(v1) => v1.set_data_contract_id(data_contract_id), + } + } +} + +impl DocumentTypeV1Getters for DocumentType { + fn document_creation_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentType::V0(_) => None, + DocumentType::V1(v1) => v1.document_creation_token_cost(), + } + } + + fn document_replacement_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentType::V0(_) => None, + DocumentType::V1(v1) => v1.document_replacement_token_cost(), + } + } + + fn document_deletion_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentType::V0(_) => None, + DocumentType::V1(v1) => v1.document_deletion_token_cost(), + } + } + + fn document_transfer_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentType::V0(_) => None, + DocumentType::V1(v1) => v1.document_transfer_token_cost(), + } + } + + fn document_update_price_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentType::V0(_) => None, + DocumentType::V1(v1) => v1.document_update_price_token_cost(), + } + } + + fn document_purchase_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentType::V0(_) => None, + DocumentType::V1(v1) => v1.document_purchase_token_cost(), + } + } +} + +impl<'a> DocumentTypeV1Getters for DocumentTypeRef<'a> { + fn document_creation_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeRef::V0(_) => None, + DocumentTypeRef::V1(v1) => v1.document_creation_token_cost(), + } + } + + fn document_replacement_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeRef::V0(_) => None, + DocumentTypeRef::V1(v1) => v1.document_replacement_token_cost(), + } + } + + fn document_deletion_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeRef::V0(_) => None, + DocumentTypeRef::V1(v1) => v1.document_deletion_token_cost(), + } + } + + fn document_transfer_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeRef::V0(_) => None, + DocumentTypeRef::V1(v1) => v1.document_transfer_token_cost(), + } + } + + fn document_update_price_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeRef::V0(_) => None, + DocumentTypeRef::V1(v1) => v1.document_update_price_token_cost(), + } + } + + fn document_purchase_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeRef::V0(_) => None, + DocumentTypeRef::V1(v1) => v1.document_purchase_token_cost(), + } + } +} + +impl<'a> DocumentTypeV1Getters for DocumentTypeMutRef<'a> { + fn document_creation_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeMutRef::V0(_) => None, + DocumentTypeMutRef::V1(v1) => v1.document_creation_token_cost(), + } + } + + fn document_replacement_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeMutRef::V0(_) => None, + DocumentTypeMutRef::V1(v1) => v1.document_replacement_token_cost(), + } + } + + fn document_deletion_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeMutRef::V0(_) => None, + DocumentTypeMutRef::V1(v1) => v1.document_deletion_token_cost(), + } + } + + fn document_transfer_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeMutRef::V0(_) => None, + DocumentTypeMutRef::V1(v1) => v1.document_transfer_token_cost(), + } + } + + fn document_update_price_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeMutRef::V0(_) => None, + DocumentTypeMutRef::V1(v1) => v1.document_update_price_token_cost(), + } + } + + fn document_purchase_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + match self { + DocumentTypeMutRef::V0(_) => None, + DocumentTypeMutRef::V1(v1) => v1.document_purchase_token_cost(), } } } diff --git a/packages/rs-dpp/src/data_contract/document_type/accessors/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/accessors/v0/mod.rs index ad134b558cb..fb696e7077e 100644 --- a/packages/rs-dpp/src/data_contract/document_type/accessors/v0/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/accessors/v0/mod.rs @@ -5,6 +5,7 @@ use crate::data_contract::document_type::property::DocumentProperty; use platform_value::{Identifier, Value}; use crate::data_contract::document_type::restricted_creation::CreationRestrictionMode; +use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; use crate::document::transfer::Transferable; use crate::identity::SecurityLevel; @@ -77,4 +78,13 @@ pub trait DocumentTypeV0Getters { /// The security level requirements fn security_level_requirement(&self) -> SecurityLevel; + + /// A reference to the json schema validator + #[cfg(feature = "validation")] + fn json_schema_validator_ref(&self) -> &StatelessJsonSchemaLazyValidator; +} + +pub trait DocumentTypeV0Setters { + /// Setting the data contract id + fn set_data_contract_id(&mut self, data_contract_id: Identifier); } diff --git a/packages/rs-dpp/src/data_contract/document_type/accessors/v1/mod.rs b/packages/rs-dpp/src/data_contract/document_type/accessors/v1/mod.rs index e17ec2f1e5f..c05f3a498b4 100644 --- a/packages/rs-dpp/src/data_contract/document_type/accessors/v1/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/accessors/v1/mod.rs @@ -36,7 +36,7 @@ pub trait DocumentTypeV1Getters { /// # Returns /// - `Some((TokenContractPosition, TokenAmount))` if an update price cost exists. /// - `None` if no cost is set for updating the price. - fn document_price_update_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; + fn document_update_price_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)>; /// Returns the token cost associated with document purchase, if applicable. /// diff --git a/packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/mod.rs b/packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/mod.rs index fcc71ce022b..e66661529ad 100644 --- a/packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/mod.rs @@ -1,13 +1,39 @@ use crate::data_contract::config::DataContractConfig; use crate::data_contract::document_type::v0::DocumentTypeV0; -use crate::data_contract::document_type::DocumentType; +use crate::data_contract::document_type::v1::DocumentTypeV1; +use crate::data_contract::document_type::{ + property_names, DocumentProperty, DocumentPropertyType, DocumentType, +}; +use crate::data_contract::errors::DataContractError; +use crate::util::json_schema::resolve_uri; use crate::validation::operations::ProtocolValidationOperation; use crate::ProtocolError; +use indexmap::IndexMap; +use platform_value::btreemap_extensions::BTreeValueMapHelper; use platform_value::{Identifier, Value}; use platform_version::version::PlatformVersion; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; mod v0; +mod v1; + +const NOT_ALLOWED_SYSTEM_PROPERTIES: [&str; 1] = ["$id"]; +const SYSTEM_PROPERTIES: [&str; 11] = [ + "$id", + "$ownerId", + "$createdAt", + "$updatedAt", + "$transferredAt", + "$createdAtBlockHeight", + "$updatedAtBlockHeight", + "$transferredAtBlockHeight", + "$createdAtCoreBlockHeight", + "$updatedAtCoreBlockHeight", + "$transferredAtCoreBlockHeight", +]; +const MAX_INDEXED_STRING_PROPERTY_LENGTH: u16 = 63; +const MAX_INDEXED_BYTE_ARRAY_PROPERTY_LENGTH: u16 = 255; +const MAX_INDEXED_ARRAY_ITEMS: usize = 1024; impl DocumentType { pub fn try_from_schema( @@ -27,7 +53,18 @@ impl DocumentType { .class_method_versions .try_from_schema { - 0 => DocumentTypeV0::try_from_schema_v0( + 0 => DocumentTypeV0::try_from_schema( + data_contract_id, + name, + schema, + schema_defs, + data_contact_config, + full_validation, + validation_operations, + platform_version, + ) + .map(|document_type| document_type.into()), + 1 => DocumentTypeV1::try_from_schema( data_contract_id, name, schema, @@ -46,3 +83,182 @@ impl DocumentType { } } } + +fn insert_values( + document_properties: &mut IndexMap, + known_required: &BTreeSet, + known_transient: &BTreeSet, + prefix: Option, + property_key: String, + property_value: &Value, + root_schema: &Value, + config: &DataContractConfig, +) -> Result<(), DataContractError> { + let mut to_visit: Vec<(Option, String, &Value)> = + vec![(prefix, property_key, property_value)]; + + while let Some((prefix, property_key, property_value)) = to_visit.pop() { + let prefixed_property_key = match prefix { + None => property_key, + Some(prefix) => [prefix, property_key].join(".").to_owned(), + }; + + let mut inner_properties = property_value.to_btree_ref_string_map()?; + + if let Some(schema_ref) = inner_properties.get_optional_str(property_names::REF)? { + let referenced_sub_schema = resolve_uri(root_schema, schema_ref)?; + + inner_properties = referenced_sub_schema.to_btree_ref_string_map()? + } + + let is_required = known_required.contains(&prefixed_property_key); + let is_transient = known_transient.contains(&prefixed_property_key); + + match DocumentPropertyType::try_from_value_map(&inner_properties, &config.into())? { + DocumentPropertyType::Object(_) => { + if let Some(properties_as_value) = inner_properties.get(property_names::PROPERTIES) + { + let properties = + properties_as_value + .as_map() + .ok_or(DataContractError::ValueWrongType( + "properties must be a map".to_string(), + ))?; + + for (object_property_key, object_property_value) in properties.iter() { + let object_property_string = object_property_key + .as_text() + .ok_or(DataContractError::KeyWrongType( + "property key must be a string".to_string(), + ))? + .to_string(); + to_visit.push(( + Some(prefixed_property_key.clone()), + object_property_string, + object_property_value, + )); + } + } + } + property_type => { + document_properties.insert( + prefixed_property_key, + DocumentProperty { + property_type, + required: is_required, + transient: is_transient, + }, + ); + } + }; + } + + Ok(()) +} + +// TODO: This is quite big +fn insert_values_nested( + document_properties: &mut IndexMap, + known_required: &BTreeSet, + known_transient: &BTreeSet, + property_key: String, + property_value: &Value, + root_schema: &Value, + config: &DataContractConfig, +) -> Result<(), DataContractError> { + let mut inner_properties = property_value.to_btree_ref_string_map()?; + + if let Some(schema_ref) = inner_properties.get_optional_str(property_names::REF)? { + let referenced_sub_schema = resolve_uri(root_schema, schema_ref)?; + + inner_properties = referenced_sub_schema.to_btree_ref_string_map()?; + } + + let is_required = known_required.contains(&property_key); + + let is_transient = known_transient.contains(&property_key); + + let property_type = + match DocumentPropertyType::try_from_value_map(&inner_properties, &config.into())? { + DocumentPropertyType::Object(_) => { + let mut nested_properties = IndexMap::new(); + if let Some(properties_as_value) = inner_properties.get(property_names::PROPERTIES) + { + let properties = + properties_as_value + .as_map() + .ok_or(DataContractError::ValueWrongType( + "properties must be a map".to_string(), + ))?; + + let mut sorted_properties: Vec<_> = properties.iter().collect(); + + sorted_properties.sort_by(|(_, value_1), (_, value_2)| { + let pos_1: u64 = value_1 + .get_integer(property_names::POSITION) + .expect("expected a position"); + let pos_2: u64 = value_2 + .get_integer(property_names::POSITION) + .expect("expected a position"); + pos_1.cmp(&pos_2) + }); + + // Create a new set with the prefix removed from the keys + let stripped_required: BTreeSet = known_required + .iter() + .filter_map(|key| { + if key.starts_with(&property_key) && key.len() > property_key.len() { + Some(key[property_key.len() + 1..].to_string()) + } else { + None + } + }) + .collect(); + + let stripped_transient: BTreeSet = known_transient + .iter() + .filter_map(|key| { + if key.starts_with(&property_key) && key.len() > property_key.len() { + Some(key[property_key.len() + 1..].to_string()) + } else { + None + } + }) + .collect(); + + for (object_property_key, object_property_value) in properties.iter() { + let object_property_string = object_property_key + .as_text() + .ok_or(DataContractError::KeyWrongType( + "property key must be a string".to_string(), + ))? + .to_string(); + + insert_values_nested( + &mut nested_properties, + &stripped_required, + &stripped_transient, + object_property_string, + object_property_value, + root_schema, + config, + )?; + } + } + + DocumentPropertyType::Object(nested_properties) + } + property_type => property_type, + }; + + document_properties.insert( + property_key, + DocumentProperty { + property_type, + required: is_required, + transient: is_transient, + }, + ); + + Ok(()) +} 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 5f7cc94c2c1..0f72d93feef 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 @@ -15,9 +15,9 @@ use crate::data_contract::document_type::v0::DocumentTypeV0; #[cfg(feature = "validation")] use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; use indexmap::IndexMap; +use std::collections::BTreeMap; #[cfg(feature = "validation")] use std::collections::HashSet; -use std::collections::{BTreeMap, BTreeSet}; use std::convert::TryInto; #[cfg(feature = "validation")] @@ -32,8 +32,12 @@ use crate::consensus::basic::document::MissingPositionsInDocumentTypePropertiesE use crate::consensus::basic::BasicError; use crate::data_contract::config::v0::DataContractConfigGettersV0; use crate::data_contract::config::DataContractConfig; +use crate::data_contract::document_type::class_methods::try_from_schema::{ + MAX_INDEXED_BYTE_ARRAY_PROPERTY_LENGTH, MAX_INDEXED_STRING_PROPERTY_LENGTH, + NOT_ALLOWED_SYSTEM_PROPERTIES, SYSTEM_PROPERTIES, +}; use crate::data_contract::document_type::class_methods::{ - consensus_or_protocol_data_contract_error, consensus_or_protocol_value_error, + consensus_or_protocol_data_contract_error, consensus_or_protocol_value_error, try_from_schema, }; use crate::data_contract::document_type::property_names::{ CAN_BE_DELETED, CREATION_RESTRICTION_MODE, DOCUMENTS_KEEP_HISTORY, DOCUMENTS_MUTABLE, @@ -43,39 +47,17 @@ use crate::data_contract::document_type::{property_names, DocumentType}; use crate::data_contract::errors::DataContractError; use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; use crate::identity::SecurityLevel; -use crate::util::json_schema::resolve_uri; #[cfg(feature = "validation")] use crate::validation::meta_validators::DOCUMENT_META_SCHEMA_V0; use crate::validation::operations::ProtocolValidationOperation; use crate::version::PlatformVersion; use crate::ProtocolError; -use platform_value::btreemap_extensions::BTreeValueMapHelper; use platform_value::{Identifier, Value}; -const NOT_ALLOWED_SYSTEM_PROPERTIES: [&str; 1] = ["$id"]; - -const SYSTEM_PROPERTIES: [&str; 11] = [ - "$id", - "$ownerId", - "$createdAt", - "$updatedAt", - "$transferredAt", - "$createdAtBlockHeight", - "$updatedAtBlockHeight", - "$transferredAtBlockHeight", - "$createdAtCoreBlockHeight", - "$updatedAtCoreBlockHeight", - "$transferredAtCoreBlockHeight", -]; - -const MAX_INDEXED_STRING_PROPERTY_LENGTH: u16 = 63; -const MAX_INDEXED_BYTE_ARRAY_PROPERTY_LENGTH: u16 = 255; -const MAX_INDEXED_ARRAY_ITEMS: usize = 1024; - impl DocumentTypeV0 { // TODO: Split into multiple functions #[allow(unused_variables)] - pub(super) fn try_from_schema_v0( + pub(super) fn try_from_schema( data_contract_id: Identifier, name: &str, schema: Value, @@ -97,7 +79,7 @@ impl DocumentTypeV0 { // TODO we are silently dropping this error when we shouldn't be // but returning this error causes tests to fail; investigate more. ProtocolError::CorruptedCodeExecution( - "validation is not enabled but is being called on try_from_schema_v0".to_string(), + "validation is not enabled but is being called on try_from_schema".to_string(), ); } @@ -259,7 +241,7 @@ impl DocumentTypeV0 { for (property_key, property_value) in property_values { // TODO: It's very inefficient. It must be done in one iteration and flattened properties // must keep a reference? We even could keep only one collection - insert_values( + try_from_schema::insert_values( &mut flattened_document_properties, &required_fields, &transient_fields, @@ -271,7 +253,7 @@ impl DocumentTypeV0 { ) .map_err(consensus_or_protocol_data_contract_error)?; - insert_values_nested( + try_from_schema::insert_values_nested( &mut document_properties, &required_fields, &transient_fields, @@ -583,185 +565,6 @@ impl DocumentTypeV0 { } } -fn insert_values( - document_properties: &mut IndexMap, - known_required: &BTreeSet, - known_transient: &BTreeSet, - prefix: Option, - property_key: String, - property_value: &Value, - root_schema: &Value, - config: &DataContractConfig, -) -> Result<(), DataContractError> { - let mut to_visit: Vec<(Option, String, &Value)> = - vec![(prefix, property_key, property_value)]; - - while let Some((prefix, property_key, property_value)) = to_visit.pop() { - let prefixed_property_key = match prefix { - None => property_key, - Some(prefix) => [prefix, property_key].join(".").to_owned(), - }; - - let mut inner_properties = property_value.to_btree_ref_string_map()?; - - if let Some(schema_ref) = inner_properties.get_optional_str(property_names::REF)? { - let referenced_sub_schema = resolve_uri(root_schema, schema_ref)?; - - inner_properties = referenced_sub_schema.to_btree_ref_string_map()? - } - - let is_required = known_required.contains(&prefixed_property_key); - let is_transient = known_transient.contains(&prefixed_property_key); - - match DocumentPropertyType::try_from_value_map(&inner_properties, &config.into())? { - DocumentPropertyType::Object(_) => { - if let Some(properties_as_value) = inner_properties.get(property_names::PROPERTIES) - { - let properties = - properties_as_value - .as_map() - .ok_or(DataContractError::ValueWrongType( - "properties must be a map".to_string(), - ))?; - - for (object_property_key, object_property_value) in properties.iter() { - let object_property_string = object_property_key - .as_text() - .ok_or(DataContractError::KeyWrongType( - "property key must be a string".to_string(), - ))? - .to_string(); - to_visit.push(( - Some(prefixed_property_key.clone()), - object_property_string, - object_property_value, - )); - } - } - } - property_type => { - document_properties.insert( - prefixed_property_key, - DocumentProperty { - property_type, - required: is_required, - transient: is_transient, - }, - ); - } - }; - } - - Ok(()) -} - -// TODO: This is quite big -fn insert_values_nested( - document_properties: &mut IndexMap, - known_required: &BTreeSet, - known_transient: &BTreeSet, - property_key: String, - property_value: &Value, - root_schema: &Value, - config: &DataContractConfig, -) -> Result<(), DataContractError> { - let mut inner_properties = property_value.to_btree_ref_string_map()?; - - if let Some(schema_ref) = inner_properties.get_optional_str(property_names::REF)? { - let referenced_sub_schema = resolve_uri(root_schema, schema_ref)?; - - inner_properties = referenced_sub_schema.to_btree_ref_string_map()?; - } - - let is_required = known_required.contains(&property_key); - - let is_transient = known_transient.contains(&property_key); - - let property_type = - match DocumentPropertyType::try_from_value_map(&inner_properties, &config.into())? { - DocumentPropertyType::Object(_) => { - let mut nested_properties = IndexMap::new(); - if let Some(properties_as_value) = inner_properties.get(property_names::PROPERTIES) - { - let properties = - properties_as_value - .as_map() - .ok_or(DataContractError::ValueWrongType( - "properties must be a map".to_string(), - ))?; - - let mut sorted_properties: Vec<_> = properties.iter().collect(); - - sorted_properties.sort_by(|(_, value_1), (_, value_2)| { - let pos_1: u64 = value_1 - .get_integer(property_names::POSITION) - .expect("expected a position"); - let pos_2: u64 = value_2 - .get_integer(property_names::POSITION) - .expect("expected a position"); - pos_1.cmp(&pos_2) - }); - - // Create a new set with the prefix removed from the keys - let stripped_required: BTreeSet = known_required - .iter() - .filter_map(|key| { - if key.starts_with(&property_key) && key.len() > property_key.len() { - Some(key[property_key.len() + 1..].to_string()) - } else { - None - } - }) - .collect(); - - let stripped_transient: BTreeSet = known_transient - .iter() - .filter_map(|key| { - if key.starts_with(&property_key) && key.len() > property_key.len() { - Some(key[property_key.len() + 1..].to_string()) - } else { - None - } - }) - .collect(); - - for (object_property_key, object_property_value) in properties.iter() { - let object_property_string = object_property_key - .as_text() - .ok_or(DataContractError::KeyWrongType( - "property key must be a string".to_string(), - ))? - .to_string(); - - insert_values_nested( - &mut nested_properties, - &stripped_required, - &stripped_transient, - object_property_string, - object_property_value, - root_schema, - config, - )?; - } - } - - DocumentPropertyType::Object(nested_properties) - } - property_type => property_type, - }; - - document_properties.insert( - property_key, - DocumentProperty { - property_type, - required: is_required, - transient: is_transient, - }, - ); - - Ok(()) -} - #[cfg(test)] mod tests { use super::*; @@ -789,7 +592,7 @@ mod tests { let config = DataContractConfig::default_for_version(platform_version) .expect("should create a default config"); - let _result = DocumentTypeV0::try_from_schema_v0( + let _result = DocumentTypeV0::try_from_schema( Identifier::new([1; 32]), "valid_name-a-b-123", schema, @@ -820,7 +623,7 @@ mod tests { let config = DataContractConfig::default_for_version(platform_version) .expect("should create a default config"); - let result = DocumentTypeV0::try_from_schema_v0( + let result = DocumentTypeV0::try_from_schema( Identifier::new([1; 32]), "", schema, @@ -862,7 +665,7 @@ mod tests { let config = DataContractConfig::default_for_version(platform_version) .expect("should create a default config"); - let result = DocumentTypeV0::try_from_schema_v0( + let result = DocumentTypeV0::try_from_schema( Identifier::new([1; 32]), &"a".repeat(65), schema, @@ -904,7 +707,7 @@ mod tests { let config = DataContractConfig::default_for_version(platform_version) .expect("should create a default config"); - let result = DocumentTypeV0::try_from_schema_v0( + let result = DocumentTypeV0::try_from_schema( Identifier::new([1; 32]), "invalid name", schema.clone(), @@ -930,7 +733,7 @@ mod tests { let config = DataContractConfig::default_for_version(platform_version) .expect("should create a default config"); - let result = DocumentTypeV0::try_from_schema_v0( + let result = DocumentTypeV0::try_from_schema( Identifier::new([1; 32]), "invalid&name", schema, diff --git a/packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v1/mod.rs b/packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v1/mod.rs new file mode 100644 index 00000000000..8e503e723c1 --- /dev/null +++ b/packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v1/mod.rs @@ -0,0 +1,791 @@ +#[cfg(feature = "validation")] +use crate::consensus::basic::data_contract::{ + DuplicateIndexNameError, InvalidIndexPropertyTypeError, InvalidIndexedPropertyConstraintError, + SystemPropertyIndexAlreadyPresentError, UndefinedIndexPropertyError, + UniqueIndicesLimitReachedError, +}; +#[cfg(feature = "validation")] +use crate::consensus::ConsensusError; +use crate::data_contract::document_type::index::Index; +use crate::data_contract::document_type::index_level::IndexLevel; +use crate::data_contract::document_type::property::{DocumentProperty, DocumentPropertyType}; +#[cfg(feature = "validation")] +use crate::data_contract::document_type::schema::validate_max_depth; +#[cfg(feature = "validation")] +use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; +use indexmap::IndexMap; +use std::collections::BTreeMap; +#[cfg(feature = "validation")] +use std::collections::HashSet; +use std::convert::TryInto; + +use crate::balances::credits::TokenAmount; +#[cfg(feature = "validation")] +use crate::consensus::basic::data_contract::ContestedUniqueIndexOnMutableDocumentTypeError; +#[cfg(feature = "validation")] +use crate::consensus::basic::data_contract::ContestedUniqueIndexWithUniqueIndexError; +#[cfg(any(test, feature = "validation"))] +use crate::consensus::basic::data_contract::InvalidDocumentTypeNameError; +#[cfg(feature = "validation")] +use crate::consensus::basic::document::MissingPositionsInDocumentTypePropertiesError; +#[cfg(feature = "validation")] +use crate::consensus::basic::BasicError; +use crate::data_contract::config::v0::DataContractConfigGettersV0; +use crate::data_contract::config::DataContractConfig; +use crate::data_contract::document_type::class_methods::try_from_schema::{ + insert_values, insert_values_nested, MAX_INDEXED_BYTE_ARRAY_PROPERTY_LENGTH, + MAX_INDEXED_STRING_PROPERTY_LENGTH, NOT_ALLOWED_SYSTEM_PROPERTIES, SYSTEM_PROPERTIES, +}; +use crate::data_contract::document_type::class_methods::{ + consensus_or_protocol_data_contract_error, consensus_or_protocol_value_error, +}; +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::token_costs::v0::TokenCostsV0; +use crate::data_contract::document_type::v1::DocumentTypeV1; +use crate::data_contract::document_type::{property_names, DocumentType}; +use crate::data_contract::errors::DataContractError; +use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; +use crate::data_contract::TokenContractPosition; +use crate::identity::SecurityLevel; +#[cfg(feature = "validation")] +use crate::validation::meta_validators::DOCUMENT_META_SCHEMA_V0; +use crate::validation::operations::ProtocolValidationOperation; +use crate::version::PlatformVersion; +use crate::ProtocolError; +use platform_value::{Identifier, Value}; + +impl DocumentTypeV1 { + // TODO: Split into multiple functions + #[allow(unused_variables)] + pub(super) fn try_from_schema( + data_contract_id: Identifier, + name: &str, + schema: Value, + schema_defs: Option<&BTreeMap>, + data_contact_config: &DataContractConfig, + full_validation: bool, // we don't need to validate if loaded from state + validation_operations: &mut Vec, + platform_version: &PlatformVersion, + ) -> Result { + // Create a full root JSON Schema from shorten contract document type schema + let root_schema = DocumentType::enrich_with_base_schema( + schema.clone(), + schema_defs.map(|defs| Value::from(defs.clone())), + platform_version, + )?; + + #[cfg(not(feature = "validation"))] + if full_validation { + // TODO we are silently dropping this error when we shouldn't be + // but returning this error causes tests to fail; investigate more. + ProtocolError::CorruptedCodeExecution( + "validation is not enabled but is being called on try_from_schema".to_string(), + ); + } + + #[cfg(feature = "validation")] + let json_schema_validator = StatelessJsonSchemaLazyValidator::new(); + + #[cfg(feature = "validation")] + if full_validation { + // Make sure a document type name is compliant + if !name + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-') + || name.is_empty() + || name.len() > 64 + { + return Err(ProtocolError::ConsensusError(Box::new( + InvalidDocumentTypeNameError::new(name.to_string()).into(), + ))); + } + + // Validate document schema depth + let mut result = validate_max_depth(&root_schema, platform_version)?; + + if !result.is_valid() { + let error = result.errors.remove(0); + + let schema_size = result.into_data()?.size; + + validation_operations.push( + ProtocolValidationOperation::DocumentTypeSchemaValidationForSize(schema_size), + ); + + return Err(ProtocolError::ConsensusError(Box::new(error))); + } + + let schema_size = result.into_data()?.size; + + validation_operations.push( + ProtocolValidationOperation::DocumentTypeSchemaValidationForSize(schema_size), + ); + + // Make sure JSON Schema is compilable + let root_json_schema = root_schema.try_to_validating_json().map_err(|e| { + ProtocolError::ConsensusError( + ConsensusError::BasicError(BasicError::ValueError(e.into())).into(), + ) + })?; + + // Validate against JSON Schema + DOCUMENT_META_SCHEMA_V0 + .validate(&root_json_schema) + .map_err(|mut errs| ConsensusError::from(errs.next().unwrap()))?; + + json_schema_validator.compile(&root_json_schema, platform_version)?; + } + + // This has already been validated, but we leave the map_err here for consistency + let schema_map = schema.to_map().map_err(|err| { + consensus_or_protocol_data_contract_error(DataContractError::InvalidContractStructure( + format!("document schema must be an object: {err}"), + )) + })?; + + // Do documents of this type keep history? (Overrides contract value) + let documents_keep_history: bool = + Value::inner_optional_bool_value(schema_map, DOCUMENTS_KEEP_HISTORY) + .map_err(consensus_or_protocol_value_error)? + .unwrap_or(data_contact_config.documents_keep_history_contract_default()); + + // Are documents of this type mutable? (Overrides contract value) + let documents_mutable: bool = + Value::inner_optional_bool_value(schema_map, DOCUMENTS_MUTABLE) + .map_err(consensus_or_protocol_value_error)? + .unwrap_or(data_contact_config.documents_mutable_contract_default()); + + // Can documents of this type be deleted? (Overrides contract value) + let documents_can_be_deleted: bool = + Value::inner_optional_bool_value(schema_map, CAN_BE_DELETED) + .map_err(consensus_or_protocol_value_error)? + .unwrap_or(data_contact_config.documents_can_be_deleted_contract_default()); + + // Are documents of this type transferable? + let documents_transferable_u8: u8 = + Value::inner_optional_integer_value(schema_map, TRANSFERABLE) + .map_err(consensus_or_protocol_value_error)? + .unwrap_or_default(); + + let documents_transferable = documents_transferable_u8.try_into()?; + + // What is the trade mode of these documents + let documents_trade_mode_u8: u8 = + Value::inner_optional_integer_value(schema_map, TRADE_MODE) + .map_err(consensus_or_protocol_value_error)? + .unwrap_or_default(); + + let trade_mode = documents_trade_mode_u8.try_into()?; + + // What is the creation restriction mode of this document type? + let documents_creation_restriction_mode_u8: u8 = + Value::inner_optional_integer_value(schema_map, CREATION_RESTRICTION_MODE) + .map_err(consensus_or_protocol_value_error)? + .unwrap_or_default(); + + let creation_restriction_mode = documents_creation_restriction_mode_u8.try_into()?; + + // Extract the properties + let property_values = Value::inner_optional_index_map::( + schema_map, + property_names::PROPERTIES, + property_names::POSITION, + ) + .map_err(consensus_or_protocol_value_error)? + .unwrap_or_default(); + + #[cfg(feature = "validation")] + if full_validation { + validation_operations.push( + ProtocolValidationOperation::DocumentTypeSchemaPropertyValidation( + property_values.values().len() as u64, + ), + ); + + // We should validate that the positions are continuous + for (pos, value) in property_values.values().enumerate() { + if value.get_integer::(property_names::POSITION)? != pos as u32 { + return Err(ConsensusError::BasicError( + BasicError::MissingPositionsInDocumentTypePropertiesError( + MissingPositionsInDocumentTypePropertiesError::new( + pos as u32, + data_contract_id, + name.to_string(), + ), + ), + ) + .into()); + } + } + } + + // Prepare internal data for efficient querying + let mut flattened_document_properties: IndexMap = IndexMap::new(); + let mut document_properties: IndexMap = IndexMap::new(); + + let required_fields = Value::inner_recursive_optional_array_of_strings( + schema_map, + "".to_string(), + property_names::PROPERTIES, + property_names::REQUIRED, + ); + + let transient_fields = Value::inner_recursive_optional_array_of_strings( + schema_map, + "".to_string(), + property_names::PROPERTIES, + property_names::TRANSIENT, + ); + + // Based on the property name, determine the type + for (property_key, property_value) in property_values { + // TODO: It's very inefficient. It must be done in one iteration and flattened properties + // must keep a reference? We even could keep only one collection + insert_values( + &mut flattened_document_properties, + &required_fields, + &transient_fields, + None, + property_key.clone(), + property_value, + &root_schema, + data_contact_config, + ) + .map_err(consensus_or_protocol_data_contract_error)?; + + insert_values_nested( + &mut document_properties, + &required_fields, + &transient_fields, + property_key, + property_value, + &root_schema, + data_contact_config, + ) + .map_err(consensus_or_protocol_data_contract_error)?; + } + + // Initialize indices + let index_values = + Value::inner_optional_array_slice_value(schema_map, property_names::INDICES) + .map_err(consensus_or_protocol_value_error)?; + + #[cfg(feature = "validation")] + let mut index_names: HashSet = HashSet::new(); + #[cfg(feature = "validation")] + let mut unique_indices_count = 0; + + #[cfg(feature = "validation")] + let mut last_non_contested_unique_index_name: Option = None; + + #[cfg(feature = "validation")] + let mut last_contested_unique_index_name: Option = None; + + #[cfg(feature = "validation")] + let mut contested_indices_count = 0; + + let indices: BTreeMap = index_values + .map(|index_values| { + index_values + .iter() + .map(|index_value| { + let index: Index = index_value + .to_map() + .map_err(consensus_or_protocol_value_error)? + .as_slice() + .try_into() + .map_err(consensus_or_protocol_data_contract_error)?; + + #[cfg(feature = "validation")] + if full_validation { + validation_operations.push( + ProtocolValidationOperation::DocumentTypeSchemaIndexValidation( + index.properties.len() as u64, + index.unique, + ), + ); + + // Unique indices produces significant load on the system during state validation + // 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 + > platform_version + .dpp + .validation + .document_type + .unique_index_limit + { + return Err(ProtocolError::ConsensusError(Box::new( + UniqueIndicesLimitReachedError::new( + name.to_string(), + platform_version + .dpp + .validation + .document_type + .unique_index_limit, + false, + ) + .into(), + ))); + } + + if let Some(last_contested_unique_index_name) = + last_contested_unique_index_name.as_ref() + { + return Err(ProtocolError::ConsensusError(Box::new( + ContestedUniqueIndexWithUniqueIndexError::new( + name.to_string(), + last_contested_unique_index_name.clone(), + index.name, + ) + .into(), + ))); + } + + if index.contested_index.is_none() { + last_non_contested_unique_index_name = Some(index.name.clone()); + } + } + + 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 let Some(last_unique_index_name) = + last_non_contested_unique_index_name.as_ref() + { + return Err(ProtocolError::ConsensusError(Box::new( + ContestedUniqueIndexWithUniqueIndexError::new( + name.to_string(), + index.name, + last_unique_index_name.clone(), + ) + .into(), + ))); + } + + if documents_mutable { + return Err(ProtocolError::ConsensusError(Box::new( + ContestedUniqueIndexOnMutableDocumentTypeError::new( + name.to_string(), + index.name, + ) + .into(), + ))); + } + + last_contested_unique_index_name = Some(index.name.clone()); + } + + // Index names must be unique for the document type + if !index_names.insert(index.name.to_owned()) { + return Err(ProtocolError::ConsensusError(Box::new( + DuplicateIndexNameError::new(name.to_string(), index.name) + .into(), + ))); + } + + // Validate indexed properties + index.properties.iter().try_for_each(|index_property| { + // Do not allow to index already indexed system properties + if NOT_ALLOWED_SYSTEM_PROPERTIES + .contains(&index_property.name.as_str()) + { + return Err(ProtocolError::ConsensusError(Box::new( + SystemPropertyIndexAlreadyPresentError::new( + name.to_owned(), + index.name.to_owned(), + index_property.name.to_owned(), + ) + .into(), + ))); + } + + // Indexed property must be defined in user schema if it's not a system one + if !SYSTEM_PROPERTIES.contains(&index_property.name.as_str()) { + let property_definition = flattened_document_properties + .get(&index_property.name) + .ok_or_else(|| { + ProtocolError::ConsensusError(Box::new( + UndefinedIndexPropertyError::new( + name.to_owned(), + index.name.to_owned(), + index_property.name.to_owned(), + ) + .into(), + )) + })?; + + // Validate indexed property type + match &property_definition.property_type { + // Array and objects aren't supported for indexing yet + DocumentPropertyType::Array(_) + | DocumentPropertyType::Object(_) + | DocumentPropertyType::VariableTypeArray(_) => { + Err(ProtocolError::ConsensusError(Box::new( + InvalidIndexPropertyTypeError::new( + name.to_owned(), + index.name.to_owned(), + index_property.name.to_owned(), + property_definition.property_type.name(), + ) + .into(), + ))) + } + // Indexed byte array size must be limited + DocumentPropertyType::ByteArray(sizes) + if sizes.max_size.is_none() + || sizes.max_size.unwrap() + > MAX_INDEXED_BYTE_ARRAY_PROPERTY_LENGTH => + { + Err(ProtocolError::ConsensusError(Box::new( + InvalidIndexedPropertyConstraintError::new( + name.to_owned(), + index.name.to_owned(), + index_property.name.to_owned(), + "maxItems".to_string(), + format!( + "should be less or equal {}", + MAX_INDEXED_BYTE_ARRAY_PROPERTY_LENGTH + ), + ) + .into(), + ))) + } + // Indexed string length must be limited + DocumentPropertyType::String(sizes) + if sizes.max_length.is_none() + || sizes.max_length.unwrap() + > MAX_INDEXED_STRING_PROPERTY_LENGTH => + { + Err(ProtocolError::ConsensusError(Box::new( + InvalidIndexedPropertyConstraintError::new( + name.to_owned(), + index.name.to_owned(), + index_property.name.to_owned(), + "maxLength".to_string(), + format!( + "should be less or equal {}", + MAX_INDEXED_STRING_PROPERTY_LENGTH + ), + ) + .into(), + ))) + } + _ => Ok(()), + } + } else { + Ok(()) + } + })?; + } + + Ok((index.name.clone(), index)) + }) + .collect::, ProtocolError>>() + }) + .transpose()? + .unwrap_or_default(); + + let index_structure = + IndexLevel::try_from_indices(indices.values(), name, platform_version)?; + + // Collect binary and identifier properties + let (identifier_paths, binary_paths) = DocumentType::find_identifier_and_binary_paths( + &document_properties, + &platform_version + .dpp + .contract_versions + .document_type_versions, + )?; + + let security_level_requirement = schema + .get_optional_integer::(property_names::SECURITY_LEVEL_REQUIREMENT) + .map_err(consensus_or_protocol_value_error)? + .map(SecurityLevel::try_from) + .transpose()? + .unwrap_or(SecurityLevel::HIGH); + + let requires_identity_encryption_bounded_key = schema + .get_optional_integer::(property_names::REQUIRES_IDENTITY_ENCRYPTION_BOUNDED_KEY) + .map_err(consensus_or_protocol_value_error)? + .map(StorageKeyRequirements::try_from) + .transpose()?; + + let requires_identity_decryption_bounded_key = schema + .get_optional_integer::(property_names::REQUIRES_IDENTITY_DECRYPTION_BOUNDED_KEY) + .map_err(consensus_or_protocol_value_error)? + .map(StorageKeyRequirements::try_from) + .transpose()?; + + let token_costs_value = schema.get_optional_value("tokenCost")?; + + let extract_cost = + |key: &str| -> Result, ProtocolError> { + token_costs_value + .and_then(|v| v.get_optional_value(key).transpose()) + .transpose()? + .map(|action_cost| { + Ok(( + action_cost.get_integer::("tokenPosition")?, + action_cost.get_integer::("amount")?, + )) + }) + .transpose() + }; + + let token_costs = TokenCostsV0 { + create: extract_cost("create")?, + replace: extract_cost("replace")?, + delete: extract_cost("delete")?, + transfer: extract_cost("transfer")?, + update_price: extract_cost("update_price")?, + purchase: extract_cost("purchase")?, + } + .into(); + + Ok(DocumentTypeV1 { + name: String::from(name), + schema, + indices, + index_structure, + flattened_properties: flattened_document_properties, + properties: document_properties, + identifier_paths, + binary_paths, + required_fields, + transient_fields, + documents_keep_history, + documents_mutable, + documents_can_be_deleted, + documents_transferable, + trade_mode, + creation_restriction_mode, + data_contract_id, + requires_identity_encryption_bounded_key, + requires_identity_decryption_bounded_key, + security_level_requirement, + #[cfg(feature = "validation")] + json_schema_validator, + token_costs, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::data_contract::document_type::DocumentTypeV0; + use assert_matches::assert_matches; + use platform_value::platform_value; + + mod document_type_name { + use super::*; + + #[test] + fn should_be_valid() { + let platform_version = PlatformVersion::latest(); + + let schema = platform_value!({ + "type": "object", + "properties": { + "valid_name": { + "type": "string", + "position": 0 + } + }, + "additionalProperties": false + }); + + let config = DataContractConfig::default_for_version(platform_version) + .expect("should create a default config"); + + let _result = DocumentTypeV1::try_from_schema( + Identifier::new([1; 32]), + "valid_name-a-b-123", + schema, + None, + &config, + true, + &mut vec![], + platform_version, + ) + .expect("should be valid"); + } + + #[test] + fn should_no_be_empty() { + let platform_version = PlatformVersion::latest(); + + let schema = platform_value!({ + "type": "object", + "properties": { + "valid_name": { + "type": "string", + "position": 0 + } + }, + "additionalProperties": false + }); + + let config = DataContractConfig::default_for_version(platform_version) + .expect("should create a default config"); + + let result = DocumentTypeV1::try_from_schema( + Identifier::new([1; 32]), + "", + schema, + None, + &config, + true, + &mut vec![], + platform_version, + ); + + assert_matches!( + result, + Err(ProtocolError::ConsensusError(boxed)) => { + assert_matches!( + boxed.as_ref(), + ConsensusError::BasicError( + BasicError::InvalidDocumentTypeNameError(InvalidDocumentTypeNameError { .. }) + ) + ) + } + ); + } + + #[test] + fn should_no_be_longer_than_64_chars() { + let platform_version = PlatformVersion::latest(); + + let schema = platform_value!({ + "type": "object", + "properties": { + "valid_name": { + "type": "string", + "position": 0 + } + }, + "additionalProperties": false + }); + + let config = DataContractConfig::default_for_version(platform_version) + .expect("should create a default config"); + + let result = DocumentTypeV1::try_from_schema( + Identifier::new([1; 32]), + &"a".repeat(65), + schema, + None, + &config, + true, + &mut vec![], + platform_version, + ); + + assert_matches!( + result, + Err(ProtocolError::ConsensusError(boxed)) => { + assert_matches!( + boxed.as_ref(), + ConsensusError::BasicError( + BasicError::InvalidDocumentTypeNameError(InvalidDocumentTypeNameError { .. }) + ) + ) + } + ); + } + + #[test] + fn should_no_be_alphanumeric() { + let platform_version = PlatformVersion::latest(); + + let schema = platform_value!({ + "type": "object", + "properties": { + "valid_name": { + "type": "string", + "position": 0 + } + }, + "additionalProperties": false + }); + + let config = DataContractConfig::default_for_version(platform_version) + .expect("should create a default config"); + + let result = DocumentTypeV0::try_from_schema( + Identifier::new([1; 32]), + "invalid name", + schema.clone(), + None, + &config, + true, + &mut vec![], + platform_version, + ); + + assert_matches!( + result, + Err(ProtocolError::ConsensusError(boxed)) => { + assert_matches!( + boxed.as_ref(), + ConsensusError::BasicError( + BasicError::InvalidDocumentTypeNameError(InvalidDocumentTypeNameError { .. }) + ) + ) + } + ); + + let config = DataContractConfig::default_for_version(platform_version) + .expect("should create a default config"); + + let result = DocumentTypeV1::try_from_schema( + Identifier::new([1; 32]), + "invalid&name", + schema, + None, + &config, + true, + &mut vec![], + platform_version, + ); + + assert_matches!( + result, + Err(ProtocolError::ConsensusError(boxed)) => { + assert_matches!( + boxed.as_ref(), + ConsensusError::BasicError( + BasicError::InvalidDocumentTypeNameError(InvalidDocumentTypeNameError { .. }) + ) + ) + } + ); + } + } +} diff --git a/packages/rs-dpp/src/data_contract/document_type/methods/versioned_methods.rs b/packages/rs-dpp/src/data_contract/document_type/methods/versioned_methods.rs index b742618bf42..e4e3666b1ca 100644 --- a/packages/rs-dpp/src/data_contract/document_type/methods/versioned_methods.rs +++ b/packages/rs-dpp/src/data_contract/document_type/methods/versioned_methods.rs @@ -27,9 +27,7 @@ use platform_value::{Identifier, ReplacementType, Value}; use platform_version::version::PlatformVersion; use std::collections::BTreeMap; -pub(super) trait DocumentTypeV0MethodsVersioned: - DocumentTypeV0Getters + DocumentTypeBasicMethods -{ +pub trait DocumentTypeV0MethodsVersioned: DocumentTypeV0Getters + DocumentTypeBasicMethods { fn create_document_from_data_v0( &self, data: Value, diff --git a/packages/rs-dpp/src/data_contract/document_type/mod.rs b/packages/rs-dpp/src/data_contract/document_type/mod.rs index ac2a7702755..2e794657cbc 100644 --- a/packages/rs-dpp/src/data_contract/document_type/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/mod.rs @@ -25,6 +25,7 @@ use crate::data_contract::document_type::methods::{ DocumentTypeBasicMethods, DocumentTypeV0Methods, }; use crate::data_contract::document_type::v0::DocumentTypeV0; +use crate::data_contract::document_type::v1::DocumentTypeV1; use crate::document::Document; use crate::fee::Credits; use crate::version::PlatformVersion; @@ -78,28 +79,33 @@ mod property_names { #[derive(Clone, Copy, Debug, PartialEq)] pub enum DocumentTypeRef<'a> { V0(&'a DocumentTypeV0), + V1(&'a DocumentTypeV1), } #[derive(Debug)] pub enum DocumentTypeMutRef<'a> { V0(&'a mut DocumentTypeV0), + V1(&'a mut DocumentTypeV1), } #[derive(Debug, Clone, PartialEq, From)] pub enum DocumentType { V0(DocumentTypeV0), + V1(DocumentTypeV1), } impl DocumentType { pub const fn as_ref(&self) -> DocumentTypeRef { match self { DocumentType::V0(v0) => DocumentTypeRef::V0(v0), + DocumentType::V1(v1) => DocumentTypeRef::V1(v1), } } pub fn as_mut_ref(&mut self) -> DocumentTypeMutRef { match self { DocumentType::V0(v0) => DocumentTypeMutRef::V0(v0), + DocumentType::V1(v1) => DocumentTypeMutRef::V1(v1), } } @@ -112,6 +118,9 @@ impl DocumentType { DocumentType::V0(v0) => { v0.prefunded_voting_balance_for_document(document, platform_version) } + DocumentType::V1(v1) => { + v1.prefunded_voting_balance_for_document(document, platform_version) + } } } } @@ -120,6 +129,7 @@ impl<'a> DocumentTypeRef<'a> { pub fn to_owned_document_type(&self) -> DocumentType { match self { DocumentTypeRef::V0(v0) => DocumentType::V0((*v0).to_owned()), + DocumentTypeRef::V1(v1) => DocumentType::V1((*v1).to_owned()), } } } diff --git a/packages/rs-dpp/src/data_contract/document_type/token_costs/mod.rs b/packages/rs-dpp/src/data_contract/document_type/token_costs/mod.rs index 9b6c24f1830..7d1158c3119 100644 --- a/packages/rs-dpp/src/data_contract/document_type/token_costs/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/token_costs/mod.rs @@ -4,12 +4,13 @@ use crate::data_contract::document_type::token_costs::accessors::{ }; use crate::data_contract::document_type::token_costs::v0::TokenCostsV0; use crate::data_contract::TokenContractPosition; +use derive_more::From; pub(crate) mod accessors; -mod v0; +pub mod v0; /// The token costs for various document operations -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, From)] pub enum TokenCosts { /// Version 0 of token costs V0(TokenCostsV0), diff --git a/packages/rs-dpp/src/data_contract/document_type/v0/accessors.rs b/packages/rs-dpp/src/data_contract/document_type/v0/accessors.rs index 39306888028..d2e23369ad2 100644 --- a/packages/rs-dpp/src/data_contract/document_type/v0/accessors.rs +++ b/packages/rs-dpp/src/data_contract/document_type/v0/accessors.rs @@ -1,4 +1,6 @@ -use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; +use crate::data_contract::document_type::accessors::{ + DocumentTypeV0Getters, DocumentTypeV0Setters, +}; use crate::data_contract::document_type::index::Index; use crate::data_contract::document_type::index_level::IndexLevel; use crate::data_contract::document_type::property::DocumentProperty; @@ -7,6 +9,7 @@ use crate::data_contract::document_type::v0::DocumentTypeV0; use platform_value::{Identifier, Value}; use crate::data_contract::document_type::restricted_creation::CreationRestrictionMode; +use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; use crate::document::transfer::Transferable; use crate::identity::SecurityLevel; @@ -104,4 +107,15 @@ impl DocumentTypeV0Getters for DocumentTypeV0 { fn security_level_requirement(&self) -> SecurityLevel { self.security_level_requirement } + + #[cfg(feature = "validation")] + fn json_schema_validator_ref(&self) -> &StatelessJsonSchemaLazyValidator { + &self.json_schema_validator + } +} + +impl DocumentTypeV0Setters for DocumentTypeV0 { + fn set_data_contract_id(&mut self, data_contract_id: Identifier) { + self.data_contract_id = data_contract_id; + } } diff --git a/packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs b/packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs index 05c6437af4f..85b2e1cbfbb 100644 --- a/packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs +++ b/packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs @@ -1,5 +1,5 @@ use crate::data_contract::document_type::accessors::{ - DocumentTypeV0Getters, DocumentTypeV1Getters, + DocumentTypeV0Getters, DocumentTypeV0Setters, DocumentTypeV1Getters, }; use crate::data_contract::document_type::index::Index; use crate::data_contract::document_type::index_level::IndexLevel; @@ -11,6 +11,7 @@ use crate::balances::credits::TokenAmount; use crate::data_contract::document_type::restricted_creation::CreationRestrictionMode; use crate::data_contract::document_type::token_costs::accessors::TokenCostGettersV0; use crate::data_contract::document_type::v1::DocumentTypeV1; +use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; use crate::data_contract::TokenContractPosition; use crate::document::transfer::Transferable; @@ -109,6 +110,17 @@ impl DocumentTypeV0Getters for DocumentTypeV1 { fn security_level_requirement(&self) -> SecurityLevel { self.security_level_requirement } + + #[cfg(feature = "validation")] + fn json_schema_validator_ref(&self) -> &StatelessJsonSchemaLazyValidator { + &self.json_schema_validator + } +} + +impl DocumentTypeV0Setters for DocumentTypeV1 { + fn set_data_contract_id(&mut self, data_contract_id: Identifier) { + self.data_contract_id = data_contract_id; + } } impl DocumentTypeV1Getters for DocumentTypeV1 { @@ -128,7 +140,7 @@ impl DocumentTypeV1Getters for DocumentTypeV1 { self.token_costs.document_transfer_token_cost() } - fn document_price_update_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { + fn document_update_price_token_cost(&self) -> Option<(TokenContractPosition, TokenAmount)> { self.token_costs.document_price_update_token_cost() } diff --git a/packages/rs-dpp/src/data_contract/methods/validate_document/v0/mod.rs b/packages/rs-dpp/src/data_contract/methods/validate_document/v0/mod.rs index e7d10410226..32914c4e23a 100644 --- a/packages/rs-dpp/src/data_contract/methods/validate_document/v0/mod.rs +++ b/packages/rs-dpp/src/data_contract/methods/validate_document/v0/mod.rs @@ -1,6 +1,6 @@ use crate::data_contract::accessors::v0::DataContractV0Getters; use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; -use crate::data_contract::document_type::{DocumentType, DocumentTypeRef}; +use crate::data_contract::document_type::DocumentType; use crate::consensus::basic::document::{ DocumentFieldMaxSizeExceededError, InvalidDocumentTypeError, MissingDocumentTypeError, @@ -46,9 +46,7 @@ impl DataContract { )); }; - let validator = match document_type { - DocumentTypeRef::V0(v0) => v0.json_schema_validator.deref(), - }; + let validator = document_type.json_schema_validator_ref().deref(); if let Some((key, size)) = value.has_data_larger_than(platform_version.system_limits.max_field_value_size) diff --git a/packages/rs-dpp/src/data_contract/methods/validate_update/v0/mod.rs b/packages/rs-dpp/src/data_contract/methods/validate_update/v0/mod.rs index 21b9fc6183b..d49f0a9e89e 100644 --- a/packages/rs-dpp/src/data_contract/methods/validate_update/v0/mod.rs +++ b/packages/rs-dpp/src/data_contract/methods/validate_update/v0/mod.rs @@ -379,7 +379,10 @@ mod tests { .document_types_mut() .get_mut("niceDocument") .unwrap() - .as_mut_ref(); + .as_mut_ref() + else { + panic!("expected v0") + }; new_document_type.documents_mutable = false; let result = old_data_contract diff --git a/packages/rs-dpp/src/data_contract/v0/accessors/mod.rs b/packages/rs-dpp/src/data_contract/v0/accessors/mod.rs index 0332d7c6cf0..51449dee5be 100644 --- a/packages/rs-dpp/src/data_contract/v0/accessors/mod.rs +++ b/packages/rs-dpp/src/data_contract/v0/accessors/mod.rs @@ -6,7 +6,9 @@ use crate::data_contract::errors::DataContractError; use crate::data_contract::v0::DataContractV0; use crate::data_contract::DocumentName; -use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; +use crate::data_contract::document_type::accessors::{ + DocumentTypeV0Getters, DocumentTypeV0Setters, +}; use platform_value::Identifier; use std::collections::BTreeMap; @@ -104,9 +106,7 @@ impl DataContractV0Setters for DataContractV0 { self.document_types .iter_mut() - .for_each(|(_, document_type)| match document_type { - DocumentType::V0(v0) => v0.data_contract_id = id, - }) + .for_each(|(_, document_type)| document_type.set_data_contract_id(id)) } fn set_version(&mut self, version: u32) { diff --git a/packages/rs-dpp/src/data_contract/v1/accessors/mod.rs b/packages/rs-dpp/src/data_contract/v1/accessors/mod.rs index 4d6260a8ad7..491f828db1c 100644 --- a/packages/rs-dpp/src/data_contract/v1/accessors/mod.rs +++ b/packages/rs-dpp/src/data_contract/v1/accessors/mod.rs @@ -9,7 +9,9 @@ use crate::data_contract::{DocumentName, GroupContractPosition, TokenContractPos use crate::block::epoch::EpochIndex; use crate::data_contract::accessors::v1::{DataContractV1Getters, DataContractV1Setters}; use crate::data_contract::associated_token::token_configuration::TokenConfiguration; -use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; +use crate::data_contract::document_type::accessors::{ + DocumentTypeV0Getters, DocumentTypeV0Setters, +}; use crate::data_contract::group::Group; use crate::identity::TimestampMillis; use crate::prelude::BlockHeight; @@ -113,9 +115,7 @@ impl DataContractV0Setters for DataContractV1 { self.document_types .iter_mut() - .for_each(|(_, document_type)| match document_type { - DocumentType::V0(v0) => v0.data_contract_id = id, - }) + .for_each(|(_, document_type)| document_type.set_data_contract_id(id)) } fn set_version(&mut self, version: u32) { diff --git a/packages/rs-dpp/src/document/extended_document/v0/mod.rs b/packages/rs-dpp/src/document/extended_document/v0/mod.rs index bf944fb3b47..d048b97a3ac 100644 --- a/packages/rs-dpp/src/document/extended_document/v0/mod.rs +++ b/packages/rs-dpp/src/document/extended_document/v0/mod.rs @@ -32,9 +32,7 @@ use std::collections::BTreeMap; use crate::data_contract::accessors::v0::DataContractV0Getters; use crate::data_contract::document_type::accessors::DocumentTypeV0Getters; -use crate::data_contract::document_type::methods::{ - DocumentTypeBasicMethods, DocumentTypeV0Methods, -}; +use crate::data_contract::document_type::methods::DocumentTypeBasicMethods; #[cfg(feature = "validation")] use crate::data_contract::validate_document::DataContractDocumentValidationMethodsV0; #[cfg(feature = "document-json-conversion")] diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_base_transaction_action/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_base_transaction_action/mod.rs new file mode 100644 index 00000000000..a93738af802 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_base_transaction_action/mod.rs @@ -0,0 +1,62 @@ +use dpp::block::block_info::BlockInfo; +use dpp::identifier::Identifier; +use dpp::validation::SimpleConsensusValidationResult; +use drive::grovedb::TransactionArg; +use drive::state_transition_action::batch::batched_transition::document_transition::document_base_transition_action::DocumentBaseTransitionAction; +use dpp::version::PlatformVersion; +use crate::error::Error; +use crate::error::execution::ExecutionError; +use crate::execution::types::state_transition_execution_context::StateTransitionExecutionContext; +use crate::execution::validation::state_transition::batch::action_validation::document::document_base_transaction_action::state_v0::DocumentBaseTransitionActionStateValidationV0; +use crate::platform_types::platform::PlatformStateRef; + +mod state_v0; + +pub trait DocumentBaseTransitionActionValidation { + fn validate_state( + &self, + platform: &PlatformStateRef, + owner_id: Identifier, + block_info: &BlockInfo, + transition_type: &str, + execution_context: &mut StateTransitionExecutionContext, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result; +} + +impl DocumentBaseTransitionActionValidation for DocumentBaseTransitionAction { + fn validate_state( + &self, + platform: &PlatformStateRef, + owner_id: Identifier, + block_info: &BlockInfo, + transition_type: &str, + execution_context: &mut StateTransitionExecutionContext, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result { + match platform_version + .drive_abci + .validation_and_processing + .state_transitions + .batch_state_transition + .document_base_transition_state_validation + { + 0 => self.validate_state_v0( + platform, + owner_id, + block_info, + transition_type, + execution_context, + transaction, + platform_version, + ), + version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { + method: "DocumentBaseTransitionAction::validate_state".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_base_transaction_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_base_transaction_action/state_v0/mod.rs new file mode 100644 index 00000000000..e26b813a71e --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_base_transaction_action/state_v0/mod.rs @@ -0,0 +1,73 @@ +use dpp::block::block_info::BlockInfo; +use dpp::consensus::ConsensusError; +use dpp::consensus::state::state_error::StateError; +use dpp::consensus::state::token::IdentityDoesNotHaveEnoughTokenBalanceError; +use dpp::identifier::Identifier; +use dpp::validation::SimpleConsensusValidationResult; +use drive::grovedb::TransactionArg; +use drive::state_transition_action::batch::batched_transition::document_transition::document_base_transition_action::{DocumentBaseTransitionAction, DocumentBaseTransitionActionAccessorsV0}; +use dpp::version::PlatformVersion; +use crate::error::Error; +use crate::execution::types::execution_operation::ValidationOperation; +use crate::execution::types::state_transition_execution_context::{StateTransitionExecutionContext, StateTransitionExecutionContextMethodsV0}; +use crate::platform_types::platform::PlatformStateRef; + +pub(in crate::execution::validation::state_transition::state_transitions::batch::action_validation) trait DocumentBaseTransitionActionStateValidationV0 { + fn validate_state_v0( + &self, + platform: &PlatformStateRef, + owner_id: Identifier, + block_info: &BlockInfo, + transition_type: &str, + execution_context: &mut StateTransitionExecutionContext, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result; +} +impl DocumentBaseTransitionActionStateValidationV0 for DocumentBaseTransitionAction { + fn validate_state_v0( + &self, + platform: &PlatformStateRef, + owner_id: Identifier, + block_info: &BlockInfo, + transition_type: &str, + execution_context: &mut StateTransitionExecutionContext, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result { + // The following was introduced with tokens, since there are no token costs before v9 there was no reason to + // create a new version for state verification + + if let Some((token_id, cost_in_tokens)) = self.token_cost() { + let (maybe_identity_token_balance, fee_result) = + platform.drive.fetch_identity_token_balance_with_costs( + token_id.to_buffer(), + owner_id.to_buffer(), + block_info, + true, + transaction, + platform_version, + )?; + let identity_token_balance = maybe_identity_token_balance.unwrap_or_default(); + execution_context + .add_operation(ValidationOperation::PrecalculatedOperation(fee_result)); + if identity_token_balance < cost_in_tokens { + return Ok(SimpleConsensusValidationResult::new_with_error( + ConsensusError::StateError( + StateError::IdentityDoesNotHaveEnoughTokenBalanceError( + IdentityDoesNotHaveEnoughTokenBalanceError::new( + token_id, + owner_id, + cost_in_tokens, + identity_token_balance, + format!("Document {} token payment", transition_type), + ), + ), + ), + )); + } + } + + Ok(SimpleConsensusValidationResult::new()) + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/state_v1/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/state_v1/mod.rs index 6bde962b057..d1e3f850d8c 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/state_v1/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/state_v1/mod.rs @@ -21,6 +21,7 @@ use drive::query::TransactionArg; use crate::error::Error; use crate::execution::types::execution_operation::ValidationOperation; use crate::execution::types::state_transition_execution_context::{StateTransitionExecutionContext, StateTransitionExecutionContextMethodsV0}; +use crate::execution::validation::state_transition::batch::action_validation::document::document_base_transaction_action::DocumentBaseTransitionActionValidation; use crate::execution::validation::state_transition::batch::state::v0::fetch_contender::fetch_contender; use crate::execution::validation::state_transition::batch::state::v0::fetch_documents::{fetch_document_with_id, has_contested_document_with_document_id}; use crate::platform_types::platform::PlatformStateRef; @@ -46,6 +47,19 @@ impl DocumentCreateTransitionActionStateValidationV1 for DocumentCreateTransitio transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result { + let validation_result = self.base().validate_state( + platform, + owner_id, + block_info, + "create", + execution_context, + transaction, + platform_version, + )?; + if !validation_result.is_valid() { + return Ok(validation_result); + } + let contract_fetch_info = self.base().data_contract_fetch_info(); let contract = &contract_fetch_info.contract; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_delete_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_delete_transition_action/state_v0/mod.rs index bd96ae4727a..7b386543d41 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_delete_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_delete_transition_action/state_v0/mod.rs @@ -17,6 +17,7 @@ use drive::state_transition_action::batch::batched_transition::document_transiti use crate::error::Error; use crate::execution::types::execution_operation::ValidationOperation; use crate::execution::types::state_transition_execution_context::{StateTransitionExecutionContext, StateTransitionExecutionContextMethodsV0}; +use crate::execution::validation::state_transition::batch::action_validation::document::document_base_transaction_action::DocumentBaseTransitionActionValidation; use crate::execution::validation::state_transition::batch::state::v0::fetch_documents::fetch_document_with_id; use crate::platform_types::platform::PlatformStateRef; @@ -36,11 +37,24 @@ impl DocumentDeleteTransitionActionStateValidationV0 for DocumentDeleteTransitio &self, platform: &PlatformStateRef, owner_id: Identifier, - _block_info: &BlockInfo, + block_info: &BlockInfo, execution_context: &mut StateTransitionExecutionContext, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result { + let validation_result = self.base().validate_state( + platform, + owner_id, + block_info, + "delete", + execution_context, + transaction, + platform_version, + )?; + if !validation_result.is_valid() { + return Ok(validation_result); + } + let contract_fetch_info = self.base().data_contract_fetch_info(); let contract = &contract_fetch_info.contract; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_purchase_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_purchase_transition_action/state_v0/mod.rs index 9d7b5d884fd..04027a3afe9 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_purchase_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_purchase_transition_action/state_v0/mod.rs @@ -10,6 +10,7 @@ use drive::grovedb::TransactionArg; use drive::state_transition_action::batch::batched_transition::document_transition::document_base_transition_action::DocumentBaseTransitionActionAccessorsV0; use crate::error::Error; use crate::execution::types::state_transition_execution_context::StateTransitionExecutionContext; +use crate::execution::validation::state_transition::batch::action_validation::document::document_base_transaction_action::DocumentBaseTransitionActionValidation; use crate::platform_types::platform::PlatformStateRef; pub(in crate::execution::validation::state_transition::state_transitions::batch::action_validation) trait DocumentPurchaseTransitionActionStateValidationV0 { @@ -28,11 +29,24 @@ impl DocumentPurchaseTransitionActionStateValidationV0 for DocumentPurchaseTrans &self, platform: &PlatformStateRef, owner_id: Identifier, - _block_info: &BlockInfo, - _execution_context: &mut StateTransitionExecutionContext, + block_info: &BlockInfo, + execution_context: &mut StateTransitionExecutionContext, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result { + let validation_result = self.base().validate_state( + platform, + owner_id, + block_info, + "purchase", + execution_context, + transaction, + platform_version, + )?; + if !validation_result.is_valid() { + return Ok(validation_result); + } + let contract_fetch_info = self.base().data_contract_fetch_info(); let contract = &contract_fetch_info.contract; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_replace_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_replace_transition_action/state_v0/mod.rs index ef3e33ad720..204c883131c 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_replace_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_replace_transition_action/state_v0/mod.rs @@ -10,6 +10,7 @@ use drive::grovedb::TransactionArg; use drive::state_transition_action::batch::batched_transition::document_transition::document_base_transition_action::DocumentBaseTransitionActionAccessorsV0; use crate::error::Error; use crate::execution::types::state_transition_execution_context::StateTransitionExecutionContext; +use crate::execution::validation::state_transition::batch::action_validation::document::document_base_transaction_action::DocumentBaseTransitionActionValidation; use crate::platform_types::platform::PlatformStateRef; pub(in crate::execution::validation::state_transition::state_transitions::batch::action_validation) trait DocumentReplaceTransitionActionStateValidationV0 { @@ -28,11 +29,23 @@ impl DocumentReplaceTransitionActionStateValidationV0 for DocumentReplaceTransit &self, platform: &PlatformStateRef, owner_id: Identifier, - _block_info: &BlockInfo, - _execution_context: &mut StateTransitionExecutionContext, + block_info: &BlockInfo, + execution_context: &mut StateTransitionExecutionContext, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result { + let validation_result = self.base().validate_state( + platform, + owner_id, + block_info, + "replace", + execution_context, + transaction, + platform_version, + )?; + if !validation_result.is_valid() { + return Ok(validation_result); + } let contract_fetch_info = self.base().data_contract_fetch_info(); let contract = &contract_fetch_info.contract; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_transfer_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_transfer_transition_action/state_v0/mod.rs index 51b68d3d3ca..638caa3fac4 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_transfer_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_transfer_transition_action/state_v0/mod.rs @@ -10,6 +10,7 @@ use drive::grovedb::TransactionArg; use drive::state_transition_action::batch::batched_transition::document_transition::document_base_transition_action::DocumentBaseTransitionActionAccessorsV0; use crate::error::Error; use crate::execution::types::state_transition_execution_context::StateTransitionExecutionContext; +use crate::execution::validation::state_transition::batch::action_validation::document::document_base_transaction_action::DocumentBaseTransitionActionValidation; use crate::platform_types::platform::PlatformStateRef; pub(in crate::execution::validation::state_transition::state_transitions::batch::action_validation) trait DocumentTransferTransitionActionStateValidationV0 { @@ -28,11 +29,23 @@ impl DocumentTransferTransitionActionStateValidationV0 for DocumentTransferTrans &self, platform: &PlatformStateRef, owner_id: Identifier, - _block_info: &BlockInfo, - _execution_context: &mut StateTransitionExecutionContext, + block_info: &BlockInfo, + execution_context: &mut StateTransitionExecutionContext, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result { + let validation_result = self.base().validate_state( + platform, + owner_id, + block_info, + "transfer", + execution_context, + transaction, + platform_version, + )?; + if !validation_result.is_valid() { + return Ok(validation_result); + } let contract_fetch_info = self.base().data_contract_fetch_info(); let contract = &contract_fetch_info.contract; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_update_price_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_update_price_transition_action/state_v0/mod.rs index 62e1ff3cbb2..48941bc5f69 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_update_price_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_update_price_transition_action/state_v0/mod.rs @@ -10,6 +10,7 @@ use drive::grovedb::TransactionArg; use drive::state_transition_action::batch::batched_transition::document_transition::document_base_transition_action::DocumentBaseTransitionActionAccessorsV0; use crate::error::Error; use crate::execution::types::state_transition_execution_context::StateTransitionExecutionContext; +use crate::execution::validation::state_transition::batch::action_validation::document::document_base_transaction_action::DocumentBaseTransitionActionValidation; use crate::platform_types::platform::PlatformStateRef; pub(in crate::execution::validation::state_transition::state_transitions::batch::action_validation) trait DocumentUpdatePriceTransitionActionStateValidationV0 { @@ -28,11 +29,23 @@ impl DocumentUpdatePriceTransitionActionStateValidationV0 for DocumentUpdatePric &self, platform: &PlatformStateRef, owner_id: Identifier, - _block_info: &BlockInfo, - _execution_context: &mut StateTransitionExecutionContext, + block_info: &BlockInfo, + execution_context: &mut StateTransitionExecutionContext, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result { + let validation_result = self.base().validate_state( + platform, + owner_id, + block_info, + "update price", + execution_context, + transaction, + platform_version, + )?; + if !validation_result.is_valid() { + return Ok(validation_result); + } let contract_fetch_info = self.base().data_contract_fetch_info(); let contract = &contract_fetch_info.contract; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/mod.rs index 32c28c43faa..ac80aafead6 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/mod.rs @@ -1,3 +1,4 @@ +mod document_base_transaction_action; pub(crate) mod document_create_transition_action; pub(crate) mod document_delete_transition_action; pub(crate) mod document_purchase_transition_action; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/data_triggers/triggers/withdrawals/v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/data_triggers/triggers/withdrawals/v0/mod.rs index e39046a6709..edc0ca1ddf8 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/data_triggers/triggers/withdrawals/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/data_triggers/triggers/withdrawals/v0/mod.rs @@ -168,6 +168,7 @@ mod tests { identity_contract_nonce: 1, document_type_name: "".to_string(), data_contract: Arc::new(DataContractFetchInfo::dpns_contract_fixture(1)), + token_cost: None, } .into(); @@ -310,6 +311,7 @@ mod tests { data_contract: Arc::new(DataContractFetchInfo::withdrawals_contract_fixture( platform_version.protocol_version, )), + token_cost: None, }), }), ); diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs index f9b24000b64..0b333ce1e1f 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs @@ -38,7 +38,10 @@ mod creation_tests { use dpp::state_transition::batch_transition::document_create_transition::DocumentCreateTransitionV0; use dpp::state_transition::batch_transition::{DocumentCreateTransition, BatchTransitionV0}; use dpp::state_transition::StateTransition; + use dpp::data_contract::accessors::v1::DataContractV1Getters; use crate::config::PlatformConfig; + use crate::execution::validation::state_transition::tests::add_tokens_to_identity; + use crate::execution::validation::state_transition::tests::create_card_game_token_contract_with_owner_identity; #[test] fn test_document_creation() { @@ -2439,4 +2442,98 @@ mod creation_tests { }; assert_eq!(consensus_error.to_string(), "Document Creation on 86LHvdC1Tqx5P97LQUSibGFqf2vnKFpB6VkqQ7oso86e:card is not allowed because of the document type's creation restriction mode Owner Only"); } + + #[test] + fn test_document_creation_paid_with_a_token() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(433); + + let platform_state = platform.state.load(); + + let (contract_owner_id, _, _) = setup_identity(&mut platform, 958, dash_to_credits!(0.1)); + + let (buyer, signer, key) = setup_identity(&mut platform, 234, dash_to_credits!(0.1)); + + let (contract, gold_token_id, _) = create_card_game_token_contract_with_owner_identity( + &mut platform, + contract_owner_id.id(), + platform_version, + ); + + assert_eq!(contract.tokens().len(), 2); + + add_tokens_to_identity(&mut platform, gold_token_id.into(), buyer.id(), 15); + + let card_document_type = contract + .document_type_for_name("card") + .expect("expected a profile document type"); + + let entropy = Bytes32::random_with_rng(&mut rng); + + let mut document = card_document_type + .random_document_with_identifier_and_entropy( + &mut rng, + buyer.id(), + entropy, + DocumentFieldFillType::DoNotFillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + platform_version, + ) + .expect("expected a random document"); + + document.set("attack", 4.into()); + document.set("defense", 7.into()); + + let documents_batch_create_transition = + BatchTransition::new_document_creation_transition_from_document( + document.clone(), + card_document_type, + entropy.0, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs index a3f69f66894..e921df6894e 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs @@ -8,12 +8,10 @@ use dpp::consensus::basic::BasicError; use dpp::consensus::state::state_error::StateError; use dpp::consensus::ConsensusError; use dpp::dash_to_credits; -use dpp::data_contract::accessors::v1::DataContractV1Getters; use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Setters; use dpp::data_contract::associated_token::token_configuration::TokenConfiguration; use dpp::data_contract::associated_token::token_configuration_convention::v0::TokenConfigurationConventionV0; -use dpp::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; use dpp::data_contract::associated_token::token_configuration_localization::TokenConfigurationLocalization; use dpp::data_contract::associated_token::token_distribution_rules::accessors::v0::TokenDistributionRulesV0Setters; use dpp::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs index f23d3725f8f..21a883ca521 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs @@ -427,6 +427,41 @@ pub(in crate::execution) mod tests { (identity, signer, critical_public_key, withdrawal_public_key) } + pub(in crate::execution) fn add_tokens_to_identity( + platform: &TempPlatform, + token_id: Identifier, + identity_id: Identifier, + balance_to_add: Credits, + ) { + let platform_version = PlatformVersion::latest(); + platform + .drive + .add_to_identity_token_balance( + token_id.to_buffer(), + identity_id.to_buffer(), + balance_to_add, + &BlockInfo::default(), + true, + None, + platform_version, + None, + ) + .expect("expected to add token balance to identity"); + platform + .drive + .add_to_token_total_supply( + token_id.to_buffer(), + balance_to_add, + true, + false, + &BlockInfo::default(), + true, + None, + platform_version, + ) + .expect("expected to add to total supply"); + } + pub(in crate::execution) fn process_state_transitions( platform: &TempPlatform, state_transitions: &[StateTransition], @@ -2325,4 +2360,31 @@ pub(in crate::execution) mod tests { (basic_token_contract, token_id.into()) } + + pub(in crate::execution) fn create_card_game_token_contract_with_owner_identity( + platform: &mut TempPlatform, + identity_id: Identifier, + platform_version: &PlatformVersion, + ) -> (DataContract, Identifier, Identifier) { + let data_contract_id = DataContract::generate_data_contract_id_v0(identity_id, 1); + + let basic_token_contract = setup_contract( + &platform.drive, + "tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json", + Some(data_contract_id.to_buffer()), + Some(identity_id.to_buffer()), + Some(|data_contract: &mut DataContract| { + data_contract.set_created_at_epoch(Some(0)); + data_contract.set_created_at(Some(0)); + data_contract.set_created_at_block_height(Some(0)); + }), + None, + Some(platform_version), + ); + + let token_id = calculate_token_id(data_contract_id.as_bytes(), 0); + let token_id_2 = calculate_token_id(data_contract_id.as_bytes(), 0); + + (basic_token_contract, token_id.into(), token_id_2.into()) + } } diff --git a/packages/rs-drive-abci/src/query/document_query/v0/mod.rs b/packages/rs-drive-abci/src/query/document_query/v0/mod.rs index 26c37964d39..ad758f773e7 100644 --- a/packages/rs-drive-abci/src/query/document_query/v0/mod.rs +++ b/packages/rs-drive-abci/src/query/document_query/v0/mod.rs @@ -183,7 +183,6 @@ mod tests { use crate::query::tests::{ assert_invalid_identifier, setup_platform, store_data_contract, store_document, }; - use assert_matches::assert_matches; use ciborium::value::Value as CborValue; use dpp::dashcore::Network; use dpp::data_contract::document_type::random_document::CreateRandomDocument; diff --git a/packages/rs-drive-abci/tests/supporting_files/contract/basic-token-with-document/basic-token-with-document.json b/packages/rs-drive-abci/tests/supporting_files/contract/basic-token-with-document/basic-token-with-document.json index b6442f453c5..1379809b590 100644 --- a/packages/rs-drive-abci/tests/supporting_files/contract/basic-token-with-document/basic-token-with-document.json +++ b/packages/rs-drive-abci/tests/supporting_files/contract/basic-token-with-document/basic-token-with-document.json @@ -10,10 +10,9 @@ "canBeDeleted": false, "transferable": 1, "tokenCost": { - "tokenPosition": 0, - "create": 10, - "replace": 1, - "delete": 1 + "create": { "tokenPosition": 0, "amount": 10 }, + "replace": { "tokenPosition": 1, "amount": 1 }, + "delete": { "tokenPosition": 1, "amount": 1 } }, "properties": { "name": { diff --git a/packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json b/packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json index 3a1c2daeccd..398555c82dd 100644 --- a/packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json +++ b/packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json @@ -1,5 +1,5 @@ { - "$format_version": "0", + "$format_version": "1", "id": "86LHvdC1Tqx5P97LQUSibGFqf2vnKFpB6VkqQ7oso86e", "ownerId": "2QjL594djCH2NyDsn45vd6yQjEDHupMKo7CEGVTHtQxU", "version": 1, @@ -10,7 +10,12 @@ "canBeDeleted": true, "transferable": 1, "tradeMode": 1, - "creationRestrictionMode": 1, + "tokenCost": { + "create": { "tokenPosition": 0, "amount": 10 }, + "delete": { "tokenPosition": 1, "amount": 1 }, + "transfer": { "tokenPosition": 1, "amount": 1 }, + "update_price": { "tokenPosition": 1, "amount": 1 } + }, "properties": { "name": { "type": "string", @@ -131,5 +136,41 @@ ], "additionalProperties": false } + }, + "tokens": { + "0": { + "$format_version": "0", + "conventions": { + "$format_version": "0", + "localizations": { + "en": { + "$format_version": "0", + "shouldCapitalize": true, + "pluralForm": "gold", + "singularForm": "gold" + } + }, + "decimals": 0 + }, + "baseSupply": 150, + "maxSupply": null + }, + "1": { + "$format_version": "0", + "conventions": { + "$format_version": "0", + "localizations": { + "en": { + "$format_version": "0", + "shouldCapitalize": true, + "pluralForm": "gas", + "singularForm": "gas" + } + }, + "decimals": 0 + }, + "baseSupply": 200, + "maxSupply": null + } } } \ No newline at end of file diff --git a/packages/rs-drive/src/drive/contract/update/update_contract/v0/mod.rs b/packages/rs-drive/src/drive/contract/update/update_contract/v0/mod.rs index 84a6c4aed45..af2cebdb0ed 100644 --- a/packages/rs-drive/src/drive/contract/update/update_contract/v0/mod.rs +++ b/packages/rs-drive/src/drive/contract/update/update_contract/v0/mod.rs @@ -13,7 +13,7 @@ use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contract::DataContract; use dpp::fee::fee_result::FeeResult; -use dpp::data_contract::document_type::methods::{DocumentTypeBasicMethods, DocumentTypeV0Methods}; +use dpp::data_contract::document_type::methods::DocumentTypeBasicMethods; use dpp::serialization::PlatformSerializableWithPlatformVersion; use dpp::fee::default_costs::CachedEpochIndexFeeVersions; diff --git a/packages/rs-drive/src/drive/tokens/estimated_costs/for_token_total_supply/v0/mod.rs b/packages/rs-drive/src/drive/tokens/estimated_costs/for_token_total_supply/v0/mod.rs index 7f022ceec82..3482cada1a9 100644 --- a/packages/rs-drive/src/drive/tokens/estimated_costs/for_token_total_supply/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/estimated_costs/for_token_total_supply/v0/mod.rs @@ -92,7 +92,7 @@ impl Drive { estimated_costs_only_with_layer_info.insert( KeyInfoPath::from_known_path(total_tokens_root_supply_path()), EstimatedLayerInformation { - tree_type: TreeType::NormalTree, + tree_type: TreeType::BigSumTree, estimated_layer_count: EstimatedLevel(10, false), estimated_layer_sizes: AllItems(DEFAULT_HASH_SIZE_U8, U64_SIZE_U32, None), }, diff --git a/packages/rs-drive/src/drive/tokens/system/remove_from_token_total_supply/v0/mod.rs b/packages/rs-drive/src/drive/tokens/system/remove_from_token_total_supply/v0/mod.rs index 1bdbe38ebb5..23591b17931 100644 --- a/packages/rs-drive/src/drive/tokens/system/remove_from_token_total_supply/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/system/remove_from_token_total_supply/v0/mod.rs @@ -5,12 +5,14 @@ use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; use crate::fees::op::LowLevelDriveOperation::GroveOperation; use crate::util::grove_operations::DirectQueryType; +use crate::util::grove_operations::QueryTarget::QueryTargetValue; use dpp::block::block_info::BlockInfo; use dpp::fee::fee_result::FeeResult; +use dpp::identifier::Identifier; use dpp::version::PlatformVersion; use grovedb::batch::QualifiedGroveDbOp; use grovedb::Element::SumItem; -use grovedb::{batch::KeyInfoPath, EstimatedLayerInformation, TransactionArg}; +use grovedb::{batch::KeyInfoPath, EstimatedLayerInformation, TransactionArg, TreeType}; use std::collections::HashMap; impl Drive { @@ -97,24 +99,40 @@ impl Drive { )?; } + let direct_query_type = if estimated_costs_only_with_layer_info.is_none() { + DirectQueryType::StatefulDirectQuery + } else { + DirectQueryType::StatelessDirectQuery { + in_tree_type: TreeType::BigSumTree, + query_target: QueryTargetValue(8), + } + }; + let path_holding_total_token_supply = total_tokens_root_supply_path(); - let total_token_supply_in_platform = self - .grove_get_raw_value_u64_from_encoded_var_vec( - (&path_holding_total_token_supply).into(), - &token_id, - DirectQueryType::StatefulDirectQuery, - transaction, - &mut drive_operations, - &platform_version.drive, - )? - .ok_or(Error::Drive(DriveError::CriticalCorruptedState( - "Total supply for Token not found in Platform", - )))?; - let new_total = total_token_supply_in_platform - .checked_sub(amount) - .ok_or(Error::Drive(DriveError::CriticalCorruptedState( - "trying to subtract an amount that would underflow total supply", - )))?; + let total_token_supply_in_platform = self.grove_get_raw_value_u64_from_encoded_var_vec( + (&path_holding_total_token_supply).into(), + &token_id, + direct_query_type, + transaction, + &mut drive_operations, + &platform_version.drive, + )?; + let new_total = if estimated_costs_only_with_layer_info.is_none() { + let total_token_supply_in_platform = total_token_supply_in_platform.ok_or( + Error::Drive(DriveError::CorruptedDriveState(format!( + "Total supply for Token not found in Platform for token {}", + Identifier::from(token_id) + ))), + )?; + + total_token_supply_in_platform.checked_sub(amount) + .ok_or(Error::Drive(DriveError::CorruptedDriveState( + format!("trying to subtract an amount {} from current amount {} that would underflow total supply for token {}", amount, total_token_supply_in_platform, Identifier::from(token_id)), + )))? + } else { + u64::MAX // This would error if we were not in estimated costs, which is what we want + }; + let path_holding_total_token_supply_vec = total_tokens_root_supply_path_vec(); let replace_op = QualifiedGroveDbOp::replace_op( path_holding_total_token_supply_vec, diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_create_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_create_transition.rs index 7a40ab8b473..92e4364028c 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_create_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_create_transition.rs @@ -1,7 +1,7 @@ use crate::error::Error; use crate::state_transition_action::action_convert_to_operations::batch::DriveHighLevelBatchOperationConverter; use crate::util::batch::DriveOperation::{ - DocumentOperation, IdentityOperation, PrefundedSpecializedBalanceOperation, + DocumentOperation, IdentityOperation, PrefundedSpecializedBalanceOperation, TokenOperation, }; use crate::util::batch::{DocumentOperationType, DriveOperation, IdentityOperationType}; use crate::util::object_size_info::DocumentInfo::DocumentOwnedInfo; @@ -15,7 +15,7 @@ use std::borrow::Cow; use crate::state_transition_action::batch::batched_transition::document_transition::document_base_transition_action::DocumentBaseTransitionActionAccessorsV0; use crate::state_transition_action::batch::batched_transition::document_transition::document_create_transition_action::{DocumentCreateTransitionAction, DocumentCreateTransitionActionAccessorsV0, DocumentFromCreateTransitionAction}; use dpp::version::PlatformVersion; -use crate::util::batch::drive_op_batch::PrefundedSpecializedBalanceOperationType; +use crate::util::batch::drive_op_batch::{PrefundedSpecializedBalanceOperationType, TokenOperationType}; use crate::util::object_size_info::DataContractInfo::DataContractFetchInfo; use crate::error::drive::DriveError; @@ -46,6 +46,8 @@ impl DriveHighLevelBatchOperationConverter for DocumentCreateTransitionAction { let also_insert_vote_poll_stored_info = self.take_should_store_contest_info(); + let document_creation_token_cost = self.base().token_cost(); + let document = Document::try_from_owned_create_transition_action( self, owner_id, @@ -63,6 +65,14 @@ impl DriveHighLevelBatchOperationConverter for DocumentCreateTransitionAction { }, )]; + if let Some((token_id, cost)) = document_creation_token_cost { + ops.push(TokenOperation(TokenOperationType::TokenBurn { + token_id, + identity_balance_holder_id: owner_id, + burn_amount: cost, + })); + } + if let Some((contested_document_resource_vote_poll, credits)) = maybe_prefunded_voting_balance { diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_delete_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_delete_transition.rs index b1622ca4d2d..d82f1af2e17 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_delete_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_delete_transition.rs @@ -1,11 +1,10 @@ use crate::state_transition_action::action_convert_to_operations::batch::DriveHighLevelBatchOperationConverter; -use crate::util::batch::DriveOperation::{DocumentOperation, IdentityOperation}; +use crate::util::batch::DriveOperation::{DocumentOperation, IdentityOperation, TokenOperation}; use crate::util::batch::{DocumentOperationType, DriveOperation, IdentityOperationType}; use crate::error::Error; use dpp::block::epoch::Epoch; - use dpp::identifier::Identifier; use crate::state_transition_action::batch::batched_transition::document_transition::document_base_transition_action::DocumentBaseTransitionActionAccessorsV0; use crate::state_transition_action::batch::batched_transition::document_transition::document_delete_transition_action::DocumentDeleteTransitionAction; @@ -13,6 +12,7 @@ use crate::state_transition_action::batch::batched_transition::document_transiti use dpp::version::PlatformVersion; use crate::util::object_size_info::{DataContractInfo, DocumentTypeInfo}; use crate::error::drive::DriveError; +use crate::util::batch::drive_op_batch::TokenOperationType; impl DriveHighLevelBatchOperationConverter for DocumentDeleteTransitionAction { fn into_high_level_batch_drive_operations<'b>( @@ -35,7 +35,9 @@ impl DriveHighLevelBatchOperationConverter for DocumentDeleteTransitionAction { let identity_contract_nonce = base.identity_contract_nonce(); - Ok(vec![ + let document_deletion_token_cost = base.token_cost(); + + let mut ops = vec![ IdentityOperation(IdentityOperationType::UpdateIdentityContractNonce { identity_id: owner_id.into_buffer(), contract_id: data_contract_id.into_buffer(), @@ -50,7 +52,17 @@ impl DriveHighLevelBatchOperationConverter for DocumentDeleteTransitionAction { base.document_type_name_owned(), ), }), - ]) + ]; + + if let Some((token_id, cost)) = document_deletion_token_cost { + ops.push(TokenOperation(TokenOperationType::TokenBurn { + token_id, + identity_balance_holder_id: owner_id, + burn_amount: cost, + })); + } + + Ok(ops) } version => Err(Error::Drive(DriveError::UnknownVersionMismatch { method: "DocumentDeleteTransitionAction::into_high_level_document_drive_operations" diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_purchase_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_purchase_transition.rs index 63e6f118aac..15af66ab529 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_purchase_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_purchase_transition.rs @@ -1,6 +1,6 @@ use crate::error::Error; use crate::state_transition_action::action_convert_to_operations::batch::DriveHighLevelBatchOperationConverter; -use crate::util::batch::DriveOperation::{DocumentOperation, IdentityOperation}; +use crate::util::batch::DriveOperation::{DocumentOperation, IdentityOperation, TokenOperation}; use crate::util::batch::{DocumentOperationType, DriveOperation, IdentityOperationType}; use crate::util::object_size_info::DocumentInfo::DocumentOwnedInfo; use crate::util::object_size_info::{DataContractInfo, DocumentTypeInfo, OwnedDocumentInfo}; @@ -13,6 +13,7 @@ use crate::state_transition_action::batch::batched_transition::document_transiti use crate::state_transition_action::batch::batched_transition::document_transition::document_purchase_transition_action::{DocumentPurchaseTransitionAction, DocumentPurchaseTransitionActionAccessorsV0}; use dpp::version::PlatformVersion; use crate::error::drive::DriveError; +use crate::util::batch::drive_op_batch::TokenOperationType; impl DriveHighLevelBatchOperationConverter for DocumentPurchaseTransitionAction { fn into_high_level_batch_drive_operations<'b>( @@ -35,6 +36,9 @@ impl DriveHighLevelBatchOperationConverter for DocumentPurchaseTransitionAction let original_owner_id = self.original_owner_id(); let purchase_amount = self.price(); let contract_fetch_info = self.base().data_contract_fetch_info(); + + let document_purchase_token_cost = self.base().token_cost(); + let document = self.document_owned(); // we are purchasing the document so the new storage flags should be on the new owner @@ -46,7 +50,7 @@ impl DriveHighLevelBatchOperationConverter for DocumentPurchaseTransitionAction Some(new_document_owner_id.to_buffer()), ); - Ok(vec![ + let mut ops = vec![ IdentityOperation(IdentityOperationType::UpdateIdentityContractNonce { identity_id: owner_id.into_buffer(), contract_id: data_contract_id.into_buffer(), @@ -71,7 +75,17 @@ impl DriveHighLevelBatchOperationConverter for DocumentPurchaseTransitionAction identity_id: original_owner_id.to_buffer(), added_balance: purchase_amount, }), - ]) + ]; + + if let Some((token_id, cost)) = document_purchase_token_cost { + ops.push(TokenOperation(TokenOperationType::TokenBurn { + token_id, + identity_balance_holder_id: owner_id, + burn_amount: cost, + })); + } + + Ok(ops) } version => Err(Error::Drive(DriveError::UnknownVersionMismatch { method: diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_replace_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_replace_transition.rs index 8cfaafa60ef..18f7efe15ae 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_replace_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_replace_transition.rs @@ -1,6 +1,6 @@ use crate::error::Error; use crate::state_transition_action::action_convert_to_operations::batch::DriveHighLevelBatchOperationConverter; -use crate::util::batch::DriveOperation::{DocumentOperation, IdentityOperation}; +use crate::util::batch::DriveOperation::{DocumentOperation, IdentityOperation, TokenOperation}; use crate::util::batch::{DocumentOperationType, DriveOperation, IdentityOperationType}; use crate::util::object_size_info::DocumentInfo::DocumentOwnedInfo; use crate::util::object_size_info::{DataContractInfo, DocumentTypeInfo, OwnedDocumentInfo}; @@ -14,6 +14,7 @@ use crate::state_transition_action::batch::batched_transition::document_transiti use crate::state_transition_action::batch::batched_transition::document_transition::document_replace_transition_action::{DocumentFromReplaceTransitionAction, DocumentReplaceTransitionAction, DocumentReplaceTransitionActionAccessorsV0}; use dpp::version::PlatformVersion; use crate::error::drive::DriveError; +use crate::util::batch::drive_op_batch::TokenOperationType; impl DriveHighLevelBatchOperationConverter for DocumentReplaceTransitionAction { fn into_high_level_batch_drive_operations<'b>( @@ -34,6 +35,7 @@ impl DriveHighLevelBatchOperationConverter for DocumentReplaceTransitionAction { let document_type_name = self.base().document_type_name().clone(); let identity_contract_nonce = self.base().identity_contract_nonce(); let contract_fetch_info = self.base().data_contract_fetch_info(); + let document_replacement_token_cost = self.base().token_cost(); let document = Document::try_from_owned_replace_transition_action( self, owner_id, @@ -43,7 +45,7 @@ impl DriveHighLevelBatchOperationConverter for DocumentReplaceTransitionAction { let storage_flags = StorageFlags::new_single_epoch(epoch.index, Some(owner_id.to_buffer())); - Ok(vec![ + let mut ops = vec![ IdentityOperation(IdentityOperationType::UpdateIdentityContractNonce { identity_id: owner_id.into_buffer(), contract_id: data_contract_id.into_buffer(), @@ -60,7 +62,17 @@ impl DriveHighLevelBatchOperationConverter for DocumentReplaceTransitionAction { contract_info: DataContractInfo::DataContractFetchInfo(contract_fetch_info), document_type_info: DocumentTypeInfo::DocumentTypeName(document_type_name), }), - ]) + ]; + + if let Some((token_id, cost)) = document_replacement_token_cost { + ops.push(TokenOperation(TokenOperationType::TokenBurn { + token_id, + identity_balance_holder_id: owner_id, + burn_amount: cost, + })); + } + + Ok(ops) } version => Err(Error::Drive(DriveError::UnknownVersionMismatch { method: diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_transfer_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_transfer_transition.rs index 65521399f83..55e0dec6117 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_transfer_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_transfer_transition.rs @@ -1,6 +1,6 @@ use crate::error::Error; use crate::state_transition_action::action_convert_to_operations::batch::DriveHighLevelBatchOperationConverter; -use crate::util::batch::DriveOperation::{DocumentOperation, IdentityOperation}; +use crate::util::batch::DriveOperation::{DocumentOperation, IdentityOperation, TokenOperation}; use crate::util::batch::{DocumentOperationType, DriveOperation, IdentityOperationType}; use crate::util::object_size_info::DocumentInfo::DocumentOwnedInfo; use crate::util::object_size_info::{DataContractInfo, DocumentTypeInfo, OwnedDocumentInfo}; @@ -14,6 +14,7 @@ use crate::state_transition_action::batch::batched_transition::document_transiti use crate::state_transition_action::batch::batched_transition::document_transition::document_transfer_transition_action::{DocumentTransferTransitionAction, DocumentTransferTransitionActionAccessorsV0}; use dpp::version::PlatformVersion; use crate::error::drive::DriveError; +use crate::util::batch::drive_op_batch::TokenOperationType; impl DriveHighLevelBatchOperationConverter for DocumentTransferTransitionAction { fn into_high_level_batch_drive_operations<'b>( @@ -34,6 +35,7 @@ impl DriveHighLevelBatchOperationConverter for DocumentTransferTransitionAction let document_type_name = self.base().document_type_name().clone(); let identity_contract_nonce = self.base().identity_contract_nonce(); let contract_fetch_info = self.base().data_contract_fetch_info(); + let document_transfer_token_cost = self.base().token_cost(); let document = self.document_owned(); // we are transferring the document so the new storage flags should be on the new owner @@ -45,7 +47,7 @@ impl DriveHighLevelBatchOperationConverter for DocumentTransferTransitionAction Some(new_document_owner_id.to_buffer()), ); - Ok(vec![ + let mut ops = vec![ IdentityOperation(IdentityOperationType::UpdateIdentityContractNonce { identity_id: owner_id.into_buffer(), contract_id: data_contract_id.into_buffer(), @@ -62,7 +64,17 @@ impl DriveHighLevelBatchOperationConverter for DocumentTransferTransitionAction contract_info: DataContractInfo::DataContractFetchInfo(contract_fetch_info), document_type_info: DocumentTypeInfo::DocumentTypeName(document_type_name), }), - ]) + ]; + + if let Some((token_id, cost)) = document_transfer_token_cost { + ops.push(TokenOperation(TokenOperationType::TokenBurn { + token_id, + identity_balance_holder_id: owner_id, + burn_amount: cost, + })); + } + + Ok(ops) } version => Err(Error::Drive(DriveError::UnknownVersionMismatch { method: diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_update_price_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_update_price_transition.rs index f01e313a8a3..b3d08472c1a 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_update_price_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/document_update_price_transition.rs @@ -1,6 +1,6 @@ use crate::error::Error; use crate::state_transition_action::action_convert_to_operations::batch::DriveHighLevelBatchOperationConverter; -use crate::util::batch::DriveOperation::{DocumentOperation, IdentityOperation}; +use crate::util::batch::DriveOperation::{DocumentOperation, IdentityOperation, TokenOperation}; use crate::util::batch::{DocumentOperationType, DriveOperation, IdentityOperationType}; use crate::util::object_size_info::DocumentInfo::DocumentOwnedInfo; use crate::util::object_size_info::{DataContractInfo, DocumentTypeInfo, OwnedDocumentInfo}; @@ -13,6 +13,7 @@ use crate::state_transition_action::batch::batched_transition::document_transiti use crate::state_transition_action::batch::batched_transition::document_transition::document_update_price_transition_action::{DocumentUpdatePriceTransitionAction, DocumentUpdatePriceTransitionActionAccessorsV0}; use dpp::version::PlatformVersion; use crate::error::drive::DriveError; +use crate::util::batch::drive_op_batch::TokenOperationType; impl DriveHighLevelBatchOperationConverter for DocumentUpdatePriceTransitionAction { fn into_high_level_batch_drive_operations<'b>( @@ -33,12 +34,13 @@ impl DriveHighLevelBatchOperationConverter for DocumentUpdatePriceTransitionActi let document_type_name = self.base().document_type_name().clone(); let identity_contract_nonce = self.base().identity_contract_nonce(); let fetch_info = self.base().data_contract_fetch_info(); + let document_update_price_token_cost = self.base().token_cost(); let document = self.document_owned(); let storage_flags = StorageFlags::new_single_epoch(epoch.index, Some(owner_id.to_buffer())); - Ok(vec![ + let mut ops = vec![ IdentityOperation(IdentityOperationType::UpdateIdentityContractNonce { identity_id: owner_id.into_buffer(), contract_id: data_contract_id.into_buffer(), @@ -55,7 +57,17 @@ impl DriveHighLevelBatchOperationConverter for DocumentUpdatePriceTransitionActi contract_info: DataContractInfo::DataContractFetchInfo(fetch_info), document_type_info: DocumentTypeInfo::DocumentTypeName(document_type_name), }), - ]) + ]; + + if let Some((token_id, cost)) = document_update_price_token_cost { + ops.push(TokenOperation(TokenOperationType::TokenBurn { + token_id, + identity_balance_holder_id: owner_id, + burn_amount: cost, + })); + } + + Ok(ops) } version => Err(Error::Drive(DriveError::UnknownVersionMismatch { method: diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/mod.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/mod.rs index c303da0bfbb..3aa64620368 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/mod.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/mod.rs @@ -2,6 +2,7 @@ use derive_more::From; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::platform_value::Identifier; +use dpp::balances::credits::TokenAmount; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contract::document_type::DocumentTypeRef; use dpp::prelude::IdentityNonce; @@ -75,4 +76,10 @@ impl DocumentBaseTransitionActionAccessorsV0 for DocumentBaseTransitionAction { DocumentBaseTransitionAction::V0(v0) => v0.identity_contract_nonce, } } + + fn token_cost(&self) -> Option<(Identifier, TokenAmount)> { + match self { + DocumentBaseTransitionAction::V0(v0) => v0.token_cost, + } + } } diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/transformer.rs index a9bc78577fc..cbd1b23c2c5 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/transformer.rs @@ -1,6 +1,8 @@ use dpp::platform_value::Identifier; use std::sync::Arc; - +use dpp::balances::credits::TokenAmount; +use dpp::data_contract::document_type::DocumentType; +use dpp::data_contract::TokenContractPosition; use dpp::ProtocolError; use dpp::state_transition::batch_transition::document_base_transition::DocumentBaseTransition; use crate::drive::contract::DataContractFetchInfo; @@ -11,12 +13,14 @@ impl DocumentBaseTransitionAction { pub fn try_from_base_transition_with_contract_lookup( value: DocumentBaseTransition, get_data_contract: impl Fn(Identifier) -> Result, ProtocolError>, + get_token_cost: impl Fn(&DocumentType) -> Option<(TokenContractPosition, TokenAmount)>, ) -> Result { match value { DocumentBaseTransition::V0(v0) => Ok( DocumentBaseTransitionActionV0::try_from_base_transition_with_contract_lookup( v0, get_data_contract, + get_token_cost, )? .into(), ), @@ -27,9 +31,10 @@ impl DocumentBaseTransitionAction { pub fn try_from_borrowed_base_transition_with_contract_lookup( value: &DocumentBaseTransition, get_data_contract: impl Fn(Identifier) -> Result, ProtocolError>, + get_token_cost: impl Fn(&DocumentType) -> Option<(TokenContractPosition, TokenAmount)>, ) -> Result { match value { - DocumentBaseTransition::V0(v0) => Ok(DocumentBaseTransitionActionV0::try_from_borrowed_base_transition_with_contract_lookup(v0, get_data_contract)?.into()), + DocumentBaseTransition::V0(v0) => Ok(DocumentBaseTransitionActionV0::try_from_borrowed_base_transition_with_contract_lookup(v0, get_data_contract, get_token_cost)?.into()), } } } diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/v0/mod.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/v0/mod.rs index 4027d10d8ed..5f51a3030fa 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/v0/mod.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/v0/mod.rs @@ -1,12 +1,12 @@ /// transformer pub mod transformer; +use dpp::balances::credits::TokenAmount; use dpp::data_contract::document_type::DocumentTypeRef; -use std::sync::Arc; - use dpp::identifier::Identifier; use dpp::prelude::IdentityNonce; use dpp::ProtocolError; +use std::sync::Arc; use crate::drive::contract::DataContractFetchInfo; @@ -21,6 +21,8 @@ pub struct DocumentBaseTransitionActionV0 { pub document_type_name: String, /// A potential data contract pub data_contract: Arc, + /// Token cost with the token_id coming first + pub token_cost: Option<(Identifier, TokenAmount)>, } /// document base transition action accessors v0 @@ -47,4 +49,7 @@ pub trait DocumentBaseTransitionActionAccessorsV0 { fn data_contract_fetch_info(&self) -> Arc; /// Identity contract nonce fn identity_contract_nonce(&self) -> IdentityNonce; + + /// Token cost + fn token_cost(&self) -> Option<(Identifier, TokenAmount)>; } diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/v0/transformer.rs index d64553ca355..05dc364dd99 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_base_transition_action/v0/transformer.rs @@ -1,9 +1,13 @@ -use std::sync::Arc; - +use dpp::balances::credits::TokenAmount; +use dpp::data_contract::accessors::v0::DataContractV0Getters; +use dpp::data_contract::document_type::DocumentType; +use dpp::data_contract::TokenContractPosition; use dpp::platform_value::Identifier; +use std::sync::Arc; use dpp::ProtocolError; use dpp::state_transition::batch_transition::document_base_transition::v0::DocumentBaseTransitionV0; +use dpp::tokens::calculate_token_id; use crate::drive::contract::DataContractFetchInfo; use crate::state_transition_action::batch::batched_transition::document_transition::document_base_transition_action::DocumentBaseTransitionActionV0; @@ -12,6 +16,7 @@ impl DocumentBaseTransitionActionV0 { pub fn try_from_base_transition_with_contract_lookup( value: DocumentBaseTransitionV0, get_data_contract: impl Fn(Identifier) -> Result, ProtocolError>, + get_token_cost: impl Fn(&DocumentType) -> Option<(TokenContractPosition, TokenAmount)>, ) -> Result { let DocumentBaseTransitionV0 { id, @@ -19,11 +24,23 @@ impl DocumentBaseTransitionActionV0 { data_contract_id, identity_contract_nonce, } = value; + let data_contract = get_data_contract(data_contract_id)?; + let document_type = data_contract + .contract + .document_type_borrowed_for_name(document_type_name.as_str())?; + let token_cost = + get_token_cost(document_type).map(|(token_contract_position, token_amount)| { + ( + calculate_token_id(data_contract_id.as_bytes(), token_contract_position).into(), + token_amount, + ) + }); Ok(DocumentBaseTransitionActionV0 { id, identity_contract_nonce, document_type_name, - data_contract: get_data_contract(data_contract_id)?, + data_contract, + token_cost, }) } @@ -31,6 +48,7 @@ impl DocumentBaseTransitionActionV0 { pub fn try_from_borrowed_base_transition_with_contract_lookup( value: &DocumentBaseTransitionV0, get_data_contract: impl Fn(Identifier) -> Result, ProtocolError>, + get_token_cost: impl Fn(&DocumentType) -> Option<(TokenContractPosition, TokenAmount)>, ) -> Result { let DocumentBaseTransitionV0 { id, @@ -38,11 +56,23 @@ impl DocumentBaseTransitionActionV0 { data_contract_id, identity_contract_nonce, } = value; + let data_contract = get_data_contract(*data_contract_id)?; + let document_type = data_contract + .contract + .document_type_borrowed_for_name(document_type_name)?; + let token_cost = + get_token_cost(document_type).map(|(token_contract_position, token_amount)| { + ( + calculate_token_id(data_contract_id.as_bytes(), token_contract_position).into(), + token_amount, + ) + }); Ok(DocumentBaseTransitionActionV0 { id: *id, identity_contract_nonce: *identity_contract_nonce, document_type_name: document_type_name.clone(), - data_contract: get_data_contract(*data_contract_id)?, + data_contract, + token_cost, }) } } diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_create_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_create_transition_action/v0/transformer.rs index 5c7540f8692..0d54449e5b2 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_create_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_create_transition_action/v0/transformer.rs @@ -1,5 +1,5 @@ use dpp::block::block_info::BlockInfo; -use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; +use dpp::data_contract::document_type::accessors::{DocumentTypeV0Getters, DocumentTypeV1Getters}; use dpp::fee::fee_result::FeeResult; use dpp::platform_value::Identifier; use grovedb::TransactionArg; @@ -37,6 +37,7 @@ impl DocumentCreateTransitionActionV0 { let base = DocumentBaseTransitionAction::try_from_base_transition_with_contract_lookup( base, get_data_contract, + |document_type| document_type.document_creation_token_cost(), )?; let document_type = base.document_type()?; @@ -133,6 +134,7 @@ impl DocumentCreateTransitionActionV0 { DocumentBaseTransitionAction::try_from_borrowed_base_transition_with_contract_lookup( base, get_data_contract, + |document_type| document_type.document_creation_token_cost(), )?; let document_type = base.document_type()?; diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_delete_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_delete_transition_action/v0/transformer.rs index e3bbfeb2893..33e9f5ec023 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_delete_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_delete_transition_action/v0/transformer.rs @@ -1,6 +1,6 @@ use dpp::platform_value::Identifier; use std::sync::Arc; - +use dpp::data_contract::document_type::accessors::DocumentTypeV1Getters; use dpp::ProtocolError; use dpp::state_transition::batch_transition::batched_transition::document_delete_transition::DocumentDeleteTransitionV0; use crate::drive::contract::DataContractFetchInfo; @@ -18,6 +18,7 @@ impl DocumentDeleteTransitionActionV0 { base: DocumentBaseTransitionAction::try_from_base_transition_with_contract_lookup( base, get_data_contract, + |document_type| document_type.document_deletion_token_cost(), )?, }) } @@ -32,6 +33,9 @@ impl DocumentDeleteTransitionActionV0 { base: DocumentBaseTransitionAction::try_from_borrowed_base_transition_with_contract_lookup( base, get_data_contract, + |document_type| { + document_type.document_deletion_token_cost() + } )?, }) } diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_purchase_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_purchase_transition_action/v0/transformer.rs index f6131860b2d..b9380c6b48d 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_purchase_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_purchase_transition_action/v0/transformer.rs @@ -3,7 +3,7 @@ use dpp::document::property_names::PRICE; use dpp::document::{property_names, Document, DocumentV0Getters, DocumentV0Setters}; use dpp::platform_value::Identifier; use std::sync::Arc; - +use dpp::data_contract::document_type::accessors::DocumentTypeV1Getters; use dpp::ProtocolError; use dpp::state_transition::batch_transition::batched_transition::document_purchase_transition::DocumentPurchaseTransitionV0; use crate::drive::contract::DataContractFetchInfo; @@ -24,6 +24,7 @@ impl DocumentPurchaseTransitionActionV0 { DocumentBaseTransitionAction::try_from_borrowed_base_transition_with_contract_lookup( base, get_data_contract, + |document_type| document_type.document_purchase_token_cost(), )?; let original_owner_id = original_document.owner_id(); diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_replace_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_replace_transition_action/v0/transformer.rs index b7d9ce25ed7..d3148b21486 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_replace_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_replace_transition_action/v0/transformer.rs @@ -2,7 +2,7 @@ use dpp::block::block_info::BlockInfo; use dpp::document::property_names; use dpp::platform_value::Identifier; use std::sync::Arc; - +use dpp::data_contract::document_type::accessors::DocumentTypeV1Getters; use dpp::identity::TimestampMillis; use dpp::prelude::{BlockHeight, CoreBlockHeight}; use dpp::ProtocolError; @@ -34,6 +34,7 @@ impl DocumentReplaceTransitionActionV0 { DocumentBaseTransitionAction::try_from_borrowed_base_transition_with_contract_lookup( base, get_data_contract, + |document_type| document_type.document_replacement_token_cost(), )?; let updated_at = if base.document_type_field_is_required(property_names::UPDATED_AT)? { Some(block_info.time_ms) diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_transfer_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_transfer_transition_action/v0/transformer.rs index 1bf629c41db..6103bfe58f0 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_transfer_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_transfer_transition_action/v0/transformer.rs @@ -3,7 +3,7 @@ use dpp::document::property_names::PRICE; use dpp::document::{property_names, Document, DocumentV0Getters, DocumentV0Setters}; use dpp::platform_value::Identifier; use std::sync::Arc; - +use dpp::data_contract::document_type::accessors::DocumentTypeV1Getters; use dpp::ProtocolError; use dpp::state_transition::batch_transition::batched_transition::document_transfer_transition::DocumentTransferTransitionV0; use crate::drive::contract::DataContractFetchInfo; @@ -27,6 +27,7 @@ impl DocumentTransferTransitionActionV0 { DocumentBaseTransitionAction::try_from_borrowed_base_transition_with_contract_lookup( base, get_data_contract, + |document_type| document_type.document_transfer_token_cost(), )?; let mut modified_document = original_document; diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_update_price_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_update_price_transition_action/v0/transformer.rs index c34bba6cb60..ffe82035d3f 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_update_price_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/document_transition/document_update_price_transition_action/v0/transformer.rs @@ -3,7 +3,7 @@ use dpp::document::property_names::PRICE; use dpp::document::{property_names, Document, DocumentV0Setters}; use dpp::platform_value::Identifier; use std::sync::Arc; - +use dpp::data_contract::document_type::accessors::DocumentTypeV1Getters; use dpp::ProtocolError; use dpp::state_transition::batch_transition::batched_transition::document_update_price_transition::DocumentUpdatePriceTransitionV0; use crate::drive::contract::DataContractFetchInfo; @@ -23,6 +23,7 @@ impl DocumentUpdatePriceTransitionActionV0 { DocumentBaseTransitionAction::try_from_borrowed_base_transition_with_contract_lookup( base, get_data_contract, + |document_type| document_type.document_update_price_token_cost(), )?; let mut modified_document = original_document; diff --git a/packages/rs-drive/src/util/grove_operations/grove_apply_batch_with_add_costs/v0/mod.rs b/packages/rs-drive/src/util/grove_operations/grove_apply_batch_with_add_costs/v0/mod.rs index 3c212ba5440..ac4f8d3e61a 100644 --- a/packages/rs-drive/src/util/grove_operations/grove_apply_batch_with_add_costs/v0/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/grove_apply_batch_with_add_costs/v0/mod.rs @@ -26,10 +26,10 @@ impl Drive { "batch is empty when trying to apply batch with add costs".to_string(), ))); } - // if ops.operations.len() < 500 { - // //no initialization - // println!("batch {}", &ops); - // } + if ops.operations.len() < 500 { + //no initialization + println!("batch {}", &ops); + } if self.config.batching_consistency_verification { let consistency_results = diff --git a/packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v2.rs b/packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v2.rs index e83ec50b2fe..ff8fa7c824f 100644 --- a/packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v2.rs +++ b/packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v2.rs @@ -27,7 +27,7 @@ pub const CONTRACT_VERSIONS_V2: DPPContractVersions = DPPContractVersions { index_levels_from_indices: 0, }, class_method_versions: DocumentTypeClassMethodVersions { - try_from_schema: 0, + try_from_schema: 1, //changed to use document type V1 create_document_types_from_document_schemas: 1, //changed to allow contracts with only tokens }, structure_version: 0, diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/mod.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/mod.rs index fb85eea357c..8ceadd2f9f6 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/mod.rs @@ -92,6 +92,7 @@ pub struct DriveAbciDocumentsStateTransitionValidationVersions { pub document_transfer_transition_structure_validation: FeatureVersion, pub document_purchase_transition_structure_validation: FeatureVersion, pub document_update_price_transition_structure_validation: FeatureVersion, + pub document_base_transition_state_validation: FeatureVersion, pub document_create_transition_state_validation: FeatureVersion, pub document_delete_transition_state_validation: FeatureVersion, pub document_replace_transition_state_validation: FeatureVersion, diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v1.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v1.rs index e5e39e28c83..2ea70f9c38c 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v1.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v1.rs @@ -123,6 +123,7 @@ pub const DRIVE_ABCI_VALIDATION_VERSIONS_V1: DriveAbciValidationVersions = document_transfer_transition_structure_validation: 0, document_purchase_transition_structure_validation: 0, document_update_price_transition_structure_validation: 0, + document_base_transition_state_validation: 0, document_create_transition_state_validation: 0, document_delete_transition_state_validation: 0, document_replace_transition_state_validation: 0, diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v2.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v2.rs index 218f3707354..c5f280b3b17 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v2.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v2.rs @@ -123,6 +123,7 @@ pub const DRIVE_ABCI_VALIDATION_VERSIONS_V2: DriveAbciValidationVersions = document_transfer_transition_structure_validation: 0, document_purchase_transition_structure_validation: 0, document_update_price_transition_structure_validation: 0, + document_base_transition_state_validation: 0, document_create_transition_state_validation: 1, document_delete_transition_state_validation: 0, document_replace_transition_state_validation: 0, diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v3.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v3.rs index 04d0270979b..758e340218d 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v3.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v3.rs @@ -123,6 +123,7 @@ pub const DRIVE_ABCI_VALIDATION_VERSIONS_V3: DriveAbciValidationVersions = document_transfer_transition_structure_validation: 0, document_purchase_transition_structure_validation: 0, document_update_price_transition_structure_validation: 0, + document_base_transition_state_validation: 0, document_create_transition_state_validation: 1, document_delete_transition_state_validation: 0, document_replace_transition_state_validation: 0, diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v4.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v4.rs index 78fe27cdb58..7b1ed68b745 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v4.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v4.rs @@ -126,6 +126,7 @@ pub const DRIVE_ABCI_VALIDATION_VERSIONS_V4: DriveAbciValidationVersions = document_transfer_transition_structure_validation: 0, document_purchase_transition_structure_validation: 0, document_update_price_transition_structure_validation: 0, + document_base_transition_state_validation: 0, document_create_transition_state_validation: 1, document_delete_transition_state_validation: 0, document_replace_transition_state_validation: 0, diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v5.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v5.rs index fcc09a5d892..41946b98be5 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v5.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v5.rs @@ -127,6 +127,7 @@ pub const DRIVE_ABCI_VALIDATION_VERSIONS_V5: DriveAbciValidationVersions = document_transfer_transition_structure_validation: 0, document_purchase_transition_structure_validation: 0, document_update_price_transition_structure_validation: 0, + document_base_transition_state_validation: 0, document_create_transition_state_validation: 1, document_delete_transition_state_validation: 0, document_replace_transition_state_validation: 0, diff --git a/packages/rs-sdk/src/platform/transition/update_price_of_document.rs b/packages/rs-sdk/src/platform/transition/update_price_of_document.rs index e0bb06e0eff..b6e651d5eec 100644 --- a/packages/rs-sdk/src/platform/transition/update_price_of_document.rs +++ b/packages/rs-sdk/src/platform/transition/update_price_of_document.rs @@ -11,7 +11,6 @@ use dpp::identity::signer::Signer; use dpp::identity::IdentityPublicKey; use dpp::state_transition::batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0; use dpp::state_transition::batch_transition::BatchTransition; -use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::state_transition::StateTransition; #[async_trait::async_trait] From 6cf04f7d76bf19284dafe91337c54a2619582026 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sat, 15 Mar 2025 05:07:17 +0700 Subject: [PATCH 3/7] another fix --- .../batch/tests/document/creation.rs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs index 0b333ce1e1f..5d0b14a4aea 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs @@ -266,7 +266,7 @@ mod creation_tests { assert_matches!( processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( + [PaidConsensusError( ConsensusError::StateError(StateError::DocumentAlreadyPresentError { .. }), _ )] @@ -1757,7 +1757,7 @@ mod creation_tests { assert_matches!( processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( + [PaidConsensusError( ConsensusError::StateError( StateError::DocumentContestDocumentWithSameIdAlreadyPresentError { .. } ), @@ -2145,10 +2145,10 @@ mod creation_tests { let result = processing_result.into_execution_results().remove(0); - let StateTransitionExecutionResult::PaidConsensusError(consensus_error, _) = result else { + let PaidConsensusError(consensus_error, _) = result else { panic!("expected a paid consensus error"); }; - assert_eq!(consensus_error.to_string(), "An Identity with the id BjNejy4r9QAvLHpQ9Yq6yRMgNymeGZ46d48fJxJbMrfW is already a contestant for the vote_poll ContestedDocumentResourceVotePoll { contract_id: GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec, document_type_name: domain, index_name: parentNameAndLabel, index_values: [string dash, string quantum] }"); + assert_eq!(consensus_error.to_string(), "An Identity with the id `BjNejy4r9QAvLHpQ9Yq6yRMgNymeGZ46d48fJxJbMrfW` is already a contestant for the vote_poll ContestedDocumentResourceVotePoll { contract_id: GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec, document_type_name: domain, index_name: parentNameAndLabel, index_values: [string dash, string quantum] }"); } #[test] @@ -2437,7 +2437,7 @@ mod creation_tests { let result = processing_result.into_execution_results().remove(0); - let StateTransitionExecutionResult::PaidConsensusError(consensus_error, _) = result else { + let PaidConsensusError(consensus_error, _) = result else { panic!("expected a paid consensus error"); }; assert_eq!(consensus_error.to_string(), "Document Creation on 86LHvdC1Tqx5P97LQUSibGFqf2vnKFpB6VkqQ7oso86e:card is not allowed because of the document type's creation restriction mode Owner Only"); @@ -2535,5 +2535,18 @@ mod creation_tests { .commit_transaction(transaction) .unwrap() .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + gold_token_id.to_buffer(), + buyer.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + + // He had 15, but spent 10 + assert_eq!(token_balance, Some(5)); } } From 2e0fbb694147bf44f295af092e217c0a5c64b2b3 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sun, 16 Mar 2025 07:44:14 +0700 Subject: [PATCH 4/7] tests passing --- .../token_configuration/v0/mod.rs | 1 + .../batch/tests/document/creation.rs | 145 +++++++- .../batch/tests/document/deletion.rs | 340 +++++++++++++++++ .../batch/tests/document/mod.rs | 2 + .../batch/tests/document/nft.rs | 342 ++++++++++++++++++ .../batch/tests/document/replacement.rs | 167 +++++++++ .../batch/tests/document/transfer.rs | 236 ++++++++++++ .../state_transitions/batch/tests/mod.rs | 5 + .../state_transition/state_transitions/mod.rs | 4 +- .../crypto-card-game-in-game-currency.json | 8 +- .../system/add_to_token_total_supply/mod.rs | 4 +- .../add_to_token_total_supply/v0/mod.rs | 6 +- .../v0/mod.rs | 8 +- .../v0/mod.rs | 2 + 14 files changed, 1252 insertions(+), 18 deletions(-) diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_configuration/v0/mod.rs b/packages/rs-dpp/src/data_contract/associated_token/token_configuration/v0/mod.rs index c46e89f8ca8..62cb3ba138b 100644 --- a/packages/rs-dpp/src/data_contract/associated_token/token_configuration/v0/mod.rs +++ b/packages/rs-dpp/src/data_contract/associated_token/token_configuration/v0/mod.rs @@ -23,6 +23,7 @@ pub struct TokenConfigurationV0 { #[serde(default = "default_change_control_rules")] pub conventions_change_rules: ChangeControlRules, /// The supply at the creation of the token + #[serde(default)] pub base_supply: TokenAmount, /// The maximum supply the token can ever have #[serde(default)] diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs index 5d0b14a4aea..df64cec4233 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs @@ -40,8 +40,6 @@ mod creation_tests { use dpp::state_transition::StateTransition; use dpp::data_contract::accessors::v1::DataContractV1Getters; use crate::config::PlatformConfig; - use crate::execution::validation::state_transition::tests::add_tokens_to_identity; - use crate::execution::validation::state_transition::tests::create_card_game_token_contract_with_owner_identity; #[test] fn test_document_creation() { @@ -2465,10 +2463,24 @@ mod creation_tests { platform_version, ); + let token_supply = platform + .drive + .fetch_token_total_supply(gold_token_id.to_buffer(), None, platform_version) + .expect("expected to fetch total supply"); + + assert_eq!(token_supply, Some(0)); + assert_eq!(contract.tokens().len(), 2); add_tokens_to_identity(&mut platform, gold_token_id.into(), buyer.id(), 15); + let token_supply = platform + .drive + .fetch_token_total_supply(gold_token_id.to_buffer(), None, platform_version) + .expect("expected to fetch total supply"); + + assert_eq!(token_supply, Some(15)); + let card_document_type = contract .document_type_for_name("card") .expect("expected a profile document type"); @@ -2545,8 +2557,135 @@ mod creation_tests { platform_version, ) .expect("expected to fetch token balance"); - + // He had 15, but spent 10 assert_eq!(token_balance, Some(5)); } + + #[test] + fn test_document_creation_not_enough_token_balance_to_create_document() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(433); + + let platform_state = platform.state.load(); + + let (contract_owner_id, _, _) = setup_identity(&mut platform, 958, dash_to_credits!(0.1)); + + let (buyer, signer, key) = setup_identity(&mut platform, 234, dash_to_credits!(0.1)); + + let (contract, gold_token_id, _) = create_card_game_token_contract_with_owner_identity( + &mut platform, + contract_owner_id.id(), + platform_version, + ); + + let token_supply = platform + .drive + .fetch_token_total_supply(gold_token_id.to_buffer(), None, platform_version) + .expect("expected to fetch total supply"); + + assert_eq!(token_supply, Some(0)); + + assert_eq!(contract.tokens().len(), 2); + + // We need 10 tokens, we have 8. + add_tokens_to_identity(&mut platform, gold_token_id.into(), buyer.id(), 8); + + let token_supply = platform + .drive + .fetch_token_total_supply(gold_token_id.to_buffer(), None, platform_version) + .expect("expected to fetch total supply"); + + assert_eq!(token_supply, Some(8)); + + let card_document_type = contract + .document_type_for_name("card") + .expect("expected a profile document type"); + + let entropy = Bytes32::random_with_rng(&mut rng); + + let mut document = card_document_type + .random_document_with_identifier_and_entropy( + &mut rng, + buyer.id(), + entropy, + DocumentFieldFillType::DoNotFillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + platform_version, + ) + .expect("expected a random document"); + + document.set("attack", 4.into()); + document.set("defense", 7.into()); + + let documents_batch_create_transition = + BatchTransition::new_document_creation_transition_from_document( + document.clone(), + card_document_type, + entropy.0, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::IdentityDoesNotHaveEnoughTokenBalanceError( + _ + )), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + gold_token_id.to_buffer(), + buyer.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + + // We should still have 8 + assert_eq!(token_balance, Some(8)); + } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs index 5b5aed9c0f0..6a36155714f 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs @@ -2,6 +2,7 @@ use super::*; mod deletion_tests { use super::*; + use crate::execution::validation::state_transition::tests::create_card_game_token_contract_with_owner_identity; #[test] fn test_document_delete_on_document_type_that_is_mutable_and_can_be_deleted() { @@ -743,4 +744,343 @@ mod deletion_tests { assert_eq!(processing_result.aggregated_fees().processing_fee, 516040); } + #[test] + fn test_document_deletion_that_needs_a_token() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(433); + + let platform_state = platform.state.load(); + + let (contract_owner_id, _, _) = setup_identity(&mut platform, 958, dash_to_credits!(0.1)); + + let (buyer, signer, key) = setup_identity(&mut platform, 234, dash_to_credits!(0.1)); + + let (contract, gold_token_id, gas_token_id) = + create_card_game_token_contract_with_owner_identity( + &mut platform, + contract_owner_id.id(), + platform_version, + ); + + assert_eq!(contract.tokens().len(), 2); + + add_tokens_to_identity(&mut platform, gold_token_id.into(), buyer.id(), 15); + add_tokens_to_identity(&mut platform, gas_token_id.into(), buyer.id(), 5); + + let card_document_type = contract + .document_type_for_name("card") + .expect("expected a profile document type"); + + let entropy = Bytes32::random_with_rng(&mut rng); + + let mut document = card_document_type + .random_document_with_identifier_and_entropy( + &mut rng, + buyer.id(), + entropy, + DocumentFieldFillType::DoNotFillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + platform_version, + ) + .expect("expected a random document"); + + document.set("attack", 4.into()); + document.set("defense", 7.into()); + + let documents_batch_create_transition = + BatchTransition::new_document_creation_transition_from_document( + document.clone(), + card_document_type, + entropy.0, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + gold_token_id.to_buffer(), + buyer.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + + // He had 15, but spent 10 + assert_eq!(token_balance, Some(5)); + + let documents_batch_deletion_transition = + BatchTransition::new_document_deletion_transition_from_document( + document, + card_document_type, + &key, + 3, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_update_serialized_transition = documents_batch_deletion_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_update_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + gas_token_id.to_buffer(), + buyer.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + + // He had 5, but spent 1 + assert_eq!(token_balance, Some(4)); + } + + #[test] + fn test_document_deletion_that_needs_a_token_not_enough_balance_to_delete() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(433); + + let platform_state = platform.state.load(); + + let (contract_owner_id, _, _) = setup_identity(&mut platform, 958, dash_to_credits!(0.1)); + + let (buyer, signer, key) = setup_identity(&mut platform, 234, dash_to_credits!(0.1)); + + let (contract, gold_token_id, gas_token_id) = + create_card_game_token_contract_with_owner_identity( + &mut platform, + contract_owner_id.id(), + platform_version, + ); + + assert_eq!(contract.tokens().len(), 2); + + add_tokens_to_identity(&mut platform, gold_token_id.into(), buyer.id(), 15); + + let card_document_type = contract + .document_type_for_name("card") + .expect("expected a profile document type"); + + let entropy = Bytes32::random_with_rng(&mut rng); + + let mut document = card_document_type + .random_document_with_identifier_and_entropy( + &mut rng, + buyer.id(), + entropy, + DocumentFieldFillType::DoNotFillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + platform_version, + ) + .expect("expected a random document"); + + document.set("attack", 4.into()); + document.set("defense", 7.into()); + + let documents_batch_create_transition = + BatchTransition::new_document_creation_transition_from_document( + document.clone(), + card_document_type, + entropy.0, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + gold_token_id.to_buffer(), + buyer.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + + // He had 15, but spent 10 + assert_eq!(token_balance, Some(5)); + + let documents_batch_deletion_transition = + BatchTransition::new_document_deletion_transition_from_document( + document, + card_document_type, + &key, + 3, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_update_serialized_transition = documents_batch_deletion_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_update_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::IdentityDoesNotHaveEnoughTokenBalanceError( + _ + )), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + gas_token_id.to_buffer(), + buyer.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + + // He still has no token balance of the gas token + assert_eq!(token_balance, None); + } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/mod.rs index 8af767d18ac..c318efd8104 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/mod.rs @@ -6,3 +6,5 @@ mod replacement; mod transfer; use super::*; + +use crate::execution::validation::state_transition::tests::create_card_game_token_contract_with_owner_identity; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/nft.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/nft.rs index bc195f9a862..6ba047d4745 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/nft.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/nft.rs @@ -2799,4 +2799,346 @@ mod nft_tests { None ); } + + #[test] + fn test_document_set_price_and_purchase_with_token_costs() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(433); + + let platform_state = platform.state.load(); + + let (contract_owner_id, _, _) = setup_identity(&mut platform, 958, dash_to_credits!(0.1)); + + let (creator, signer, key) = setup_identity(&mut platform, 234, dash_to_credits!(0.1)); + + let (purchaser, recipient_signer, recipient_key) = + setup_identity(&mut platform, 450, dash_to_credits!(1.0)); + + let (contract, gold_token_id, gas_token_id) = + create_card_game_token_contract_with_owner_identity( + &mut platform, + contract_owner_id.id(), + platform_version, + ); + + let token_supply = platform + .drive + .fetch_token_total_supply(gold_token_id.to_buffer(), None, platform_version) + .expect("expected to fetch total supply"); + + assert_eq!(token_supply, Some(0)); + + assert_eq!(contract.tokens().len(), 2); + + add_tokens_to_identity(&mut platform, gold_token_id.into(), creator.id(), 15); + add_tokens_to_identity(&mut platform, gas_token_id.into(), creator.id(), 5); + add_tokens_to_identity(&mut platform, gold_token_id.into(), purchaser.id(), 5); + + let token_supply = platform + .drive + .fetch_token_total_supply(gold_token_id.to_buffer(), None, platform_version) + .expect("expected to fetch total supply"); + + assert_eq!(token_supply, Some(20)); + + let card_document_type = contract + .document_type_for_name("card") + .expect("expected a profile document type"); + + let entropy = Bytes32::random_with_rng(&mut rng); + + let mut document = card_document_type + .random_document_with_identifier_and_entropy( + &mut rng, + creator.id(), + entropy, + DocumentFieldFillType::DoNotFillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + platform_version, + ) + .expect("expected a random document"); + + document.set("attack", 4.into()); + document.set("defense", 7.into()); + + let documents_batch_create_transition = + BatchTransition::new_document_creation_transition_from_document( + document.clone(), + card_document_type, + entropy.0, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + let sender_documents_sql_string = + format!("select * from card where $ownerId == '{}'", creator.id()); + + let query_sender_identity_documents = DriveDocumentQuery::from_sql_expr( + sender_documents_sql_string.as_str(), + &contract, + Some(&platform.config.drive), + ) + .expect("expected document query"); + + let receiver_documents_sql_string = + format!("select * from card where $ownerId == '{}'", purchaser.id()); + + let query_receiver_identity_documents = DriveDocumentQuery::from_sql_expr( + receiver_documents_sql_string.as_str(), + &contract, + Some(&platform.config.drive), + ) + .expect("expected document query"); + + let query_sender_results = platform + .drive + .query_documents( + query_sender_identity_documents.clone(), + None, + false, + None, + None, + ) + .expect("expected query result"); + + let query_receiver_results = platform + .drive + .query_documents( + query_receiver_identity_documents.clone(), + None, + false, + None, + None, + ) + .expect("expected query result"); + + // We expect the sender to have 1 document, and the receiver to have none + assert_eq!(query_sender_results.documents().len(), 1); + + assert_eq!(query_receiver_results.documents().len(), 0); + + document.set_revision(Some(2)); + + let documents_batch_update_price_transition = + BatchTransition::new_document_update_price_transition_from_document( + document.clone(), + card_document_type, + dash_to_credits!(0.1), + &key, + 3, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition for the update price"); + + let documents_batch_transfer_serialized_transition = + documents_batch_update_price_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_transfer_serialized_transition.clone()], + &platform_state, + &BlockInfo::default_with_time(50000000), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + let query_sender_results = platform + .drive + .query_documents( + query_sender_identity_documents.clone(), + None, + false, + None, + None, + ) + .expect("expected query result"); + + let query_receiver_results = platform + .drive + .query_documents( + query_receiver_identity_documents.clone(), + None, + false, + None, + None, + ) + .expect("expected query result"); + + // We expect the sender to still have their document, and the receiver to have none + assert_eq!(query_sender_results.documents().len(), 1); + + assert_eq!(query_receiver_results.documents().len(), 0); + + // The sender document should have the desired price + + let mut document = query_sender_results.documents_owned().remove(0); + + let price: Credits = document + .properties() + .get_integer("$price") + .expect("expected to get back price"); + + assert_eq!(dash_to_credits!(0.1), price); + + // At this point we want to have the receiver purchase the document + + document.set_revision(Some(3)); + + let documents_batch_purchase_transition = + BatchTransition::new_document_purchase_transition_from_document( + document.clone(), + card_document_type, + purchaser.id(), + dash_to_credits!(0.1), //same price as requested + &recipient_key, + 1, // 1 because he's never done anything + 0, + &recipient_signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition for the purchase"); + + let documents_batch_purchase_serialized_transition = documents_batch_purchase_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_purchase_serialized_transition], + &platform_state, + &BlockInfo::default_with_time(50000000), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let query_sender_results = platform + .drive + .query_documents(query_sender_identity_documents, None, false, None, None) + .expect("expected query result"); + + let query_receiver_results = platform + .drive + .query_documents(query_receiver_identity_documents, None, false, None, None) + .expect("expected query result"); + + // We expect the sender to have no documents, and the receiver to have 1 + assert_eq!(query_sender_results.documents().len(), 0); + + assert_eq!(query_receiver_results.documents().len(), 1); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + gas_token_id.to_buffer(), + purchaser.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + + // He had never had any gas + assert_eq!(token_balance, None); + + let gold_token_balance = platform + .drive + .fetch_identity_token_balance( + gold_token_id.to_buffer(), + purchaser.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + + // It costs 3 to purchase on top of the credits and he had 5 + assert_eq!(gold_token_balance, Some(2)); + } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/replacement.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/replacement.rs index 62bc1440bf6..ec5a7071a5b 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/replacement.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/replacement.rs @@ -1928,4 +1928,171 @@ mod replacement_tests { .join(" | ") ); } + + #[test] + fn test_document_replace_on_document_type_that_requires_a_token() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(433); + + let platform_state = platform.state.load(); + + let (contract_owner_id, _, _) = setup_identity(&mut platform, 958, dash_to_credits!(0.1)); + + let (creator, signer, key) = setup_identity(&mut platform, 234, dash_to_credits!(0.1)); + + let (contract, gold_token_id, gas_token_id) = + create_card_game_token_contract_with_owner_identity( + &mut platform, + contract_owner_id.id(), + platform_version, + ); + + let token_supply = platform + .drive + .fetch_token_total_supply(gold_token_id.to_buffer(), None, platform_version) + .expect("expected to fetch total supply"); + + assert_eq!(token_supply, Some(0)); + + assert_eq!(contract.tokens().len(), 2); + + add_tokens_to_identity(&mut platform, gold_token_id.into(), creator.id(), 15); + add_tokens_to_identity(&mut platform, gas_token_id.into(), creator.id(), 5); + + let card_document_type = contract + .document_type_for_name("card") + .expect("expected a profile document type"); + + let entropy = Bytes32::random_with_rng(&mut rng); + + let mut document = card_document_type + .random_document_with_identifier_and_entropy( + &mut rng, + creator.id(), + entropy, + DocumentFieldFillType::DoNotFillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + platform_version, + ) + .expect("expected a random document"); + + document.set("attack", 4.into()); + document.set("defense", 7.into()); + + let mut altered_document = document.clone(); + + altered_document.increment_revision().unwrap(); + altered_document.set("attack", 5.into()); + + let documents_batch_create_transition = + BatchTransition::new_document_creation_transition_from_document( + document, + card_document_type, + entropy.0, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let documents_batch_update_transition = + BatchTransition::new_document_replacement_transition_from_document( + altered_document, + card_document_type, + &key, + 3, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_update_serialized_transition = documents_batch_update_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_update_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + gas_token_id.to_buffer(), + creator.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + + // He had 5, but spent 2 + assert_eq!(token_balance, Some(3)); + } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/transfer.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/transfer.rs index f45b6c25412..1c788428d1a 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/transfer.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/transfer.rs @@ -1016,4 +1016,240 @@ mod transfer_tests { .join(" | ") ); } + #[test] + fn test_document_transfer_on_document_that_needs_a_token() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(433); + + let platform_state = platform.state.load(); + + let (contract_owner_id, _, _) = setup_identity(&mut platform, 958, dash_to_credits!(0.1)); + + let (creator, signer, key) = setup_identity(&mut platform, 234, dash_to_credits!(0.1)); + + let (receiver, _, _) = setup_identity(&mut platform, 450, dash_to_credits!(0.1)); + + let (contract, gold_token_id, gas_token_id) = + create_card_game_token_contract_with_owner_identity( + &mut platform, + contract_owner_id.id(), + platform_version, + ); + + let token_supply = platform + .drive + .fetch_token_total_supply(gold_token_id.to_buffer(), None, platform_version) + .expect("expected to fetch total supply"); + + assert_eq!(token_supply, Some(0)); + + assert_eq!(contract.tokens().len(), 2); + + add_tokens_to_identity(&mut platform, gold_token_id.into(), creator.id(), 15); + add_tokens_to_identity(&mut platform, gas_token_id.into(), creator.id(), 5); + + let token_supply = platform + .drive + .fetch_token_total_supply(gold_token_id.to_buffer(), None, platform_version) + .expect("expected to fetch total supply"); + + assert_eq!(token_supply, Some(15)); + + let card_document_type = contract + .document_type_for_name("card") + .expect("expected a profile document type"); + + let entropy = Bytes32::random_with_rng(&mut rng); + + let mut document = card_document_type + .random_document_with_identifier_and_entropy( + &mut rng, + creator.id(), + entropy, + DocumentFieldFillType::DoNotFillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + platform_version, + ) + .expect("expected a random document"); + + document.set("attack", 4.into()); + document.set("defense", 7.into()); + + let documents_batch_create_transition = + BatchTransition::new_document_creation_transition_from_document( + document.clone(), + card_document_type, + entropy.0, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_eq!(processing_result.valid_count(), 1); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let sender_documents_sql_string = + format!("select * from card where $ownerId == '{}'", creator.id()); + + let query_sender_identity_documents = DriveDocumentQuery::from_sql_expr( + sender_documents_sql_string.as_str(), + &contract, + Some(&platform.config.drive), + ) + .expect("expected document query"); + + let receiver_documents_sql_string = + format!("select * from card where $ownerId == '{}'", receiver.id()); + + let query_receiver_identity_documents = DriveDocumentQuery::from_sql_expr( + receiver_documents_sql_string.as_str(), + &contract, + Some(&platform.config.drive), + ) + .expect("expected document query"); + + let query_sender_results = platform + .drive + .query_documents( + query_sender_identity_documents.clone(), + None, + false, + None, + None, + ) + .expect("expected query result"); + + let query_receiver_results = platform + .drive + .query_documents( + query_receiver_identity_documents.clone(), + None, + false, + None, + None, + ) + .expect("expected query result"); + + // We expect the sender to have 1 document, and the receiver to have none + assert_eq!(query_sender_results.documents().len(), 1); + + assert_eq!(query_receiver_results.documents().len(), 0); + + document.set_revision(Some(2)); + + let documents_batch_transfer_transition = + BatchTransition::new_document_transfer_transition_from_document( + document, + card_document_type, + receiver.id(), + &key, + 3, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition for transfer"); + + let documents_batch_transfer_serialized_transition = documents_batch_transfer_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![documents_batch_transfer_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let query_sender_results = platform + .drive + .query_documents(query_sender_identity_documents, None, false, None, None) + .expect("expected query result"); + + let query_receiver_results = platform + .drive + .query_documents(query_receiver_identity_documents, None, false, None, None) + .expect("expected query result"); + + // We expect the sender to have no documents, and the receiver to have 1 + assert_eq!( + ( + query_sender_results.documents().len(), + query_receiver_results.documents().len() + ), + (0, 1) + ); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + gas_token_id.to_buffer(), + creator.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + + // He had 5, but spent 1 + assert_eq!(token_balance, Some(4)); + } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/mod.rs index 50cae04a67b..cfbea99d230 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/mod.rs @@ -23,12 +23,17 @@ use dpp::state_transition::batch_transition::methods::v0::DocumentsBatchTransiti use drive::drive::document::query::QueryDocumentsOutcomeV0Methods; use drive::drive::document::query::QueryDocumentsWithFlagsOutcomeV0Methods; +use crate::execution::validation::state_transition::tests::add_tokens_to_identity; use crate::execution::validation::state_transition::tests::setup_identity; use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult; +use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult::PaidConsensusError; use crate::test::helpers::setup::TestPlatformBuilder; use assert_matches::assert_matches; use dpp::block::block_info::BlockInfo; +use dpp::consensus::state::state_error::StateError; +use dpp::consensus::ConsensusError; use dpp::dash_to_credits; +use dpp::data_contract::accessors::v1::DataContractV1Getters; use dpp::document::transfer::Transferable; use dpp::fee::fee_result::BalanceChange; use dpp::fee::Credits; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs index 21a883ca521..1e867244856 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs @@ -454,8 +454,8 @@ pub(in crate::execution) mod tests { balance_to_add, true, false, - &BlockInfo::default(), true, + &BlockInfo::default(), None, platform_version, ) @@ -2383,7 +2383,7 @@ pub(in crate::execution) mod tests { ); let token_id = calculate_token_id(data_contract_id.as_bytes(), 0); - let token_id_2 = calculate_token_id(data_contract_id.as_bytes(), 0); + let token_id_2 = calculate_token_id(data_contract_id.as_bytes(), 1); (basic_token_contract, token_id.into(), token_id_2.into()) } diff --git a/packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json b/packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json index 398555c82dd..90cb39d749c 100644 --- a/packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json +++ b/packages/rs-drive-abci/tests/supporting_files/contract/crypto-card-game/crypto-card-game-in-game-currency.json @@ -6,15 +6,17 @@ "documentSchemas": { "card": { "type": "object", - "documentsMutable": false, + "documentsMutable": true, "canBeDeleted": true, "transferable": 1, "tradeMode": 1, "tokenCost": { "create": { "tokenPosition": 0, "amount": 10 }, "delete": { "tokenPosition": 1, "amount": 1 }, + "replace": { "tokenPosition": 1, "amount": 2 }, "transfer": { "tokenPosition": 1, "amount": 1 }, - "update_price": { "tokenPosition": 1, "amount": 1 } + "update_price": { "tokenPosition": 1, "amount": 1 }, + "purchase": { "tokenPosition": 0, "amount": 3 } }, "properties": { "name": { @@ -152,7 +154,6 @@ }, "decimals": 0 }, - "baseSupply": 150, "maxSupply": null }, "1": { @@ -169,7 +170,6 @@ }, "decimals": 0 }, - "baseSupply": 200, "maxSupply": null } } diff --git a/packages/rs-drive/src/drive/tokens/system/add_to_token_total_supply/mod.rs b/packages/rs-drive/src/drive/tokens/system/add_to_token_total_supply/mod.rs index a2b7430d0ba..5d26155ab14 100644 --- a/packages/rs-drive/src/drive/tokens/system/add_to_token_total_supply/mod.rs +++ b/packages/rs-drive/src/drive/tokens/system/add_to_token_total_supply/mod.rs @@ -20,8 +20,8 @@ impl Drive { amount: TokenAmount, allow_first_mint: bool, allow_saturation: bool, - block_info: &BlockInfo, apply: bool, + block_info: &BlockInfo, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result<(FeeResult, TokenAmount), Error> { @@ -37,8 +37,8 @@ impl Drive { amount, allow_first_mint, allow_saturation, - block_info, apply, + block_info, transaction, platform_version, ), diff --git a/packages/rs-drive/src/drive/tokens/system/add_to_token_total_supply/v0/mod.rs b/packages/rs-drive/src/drive/tokens/system/add_to_token_total_supply/v0/mod.rs index df354270cf3..226eaa3e438 100644 --- a/packages/rs-drive/src/drive/tokens/system/add_to_token_total_supply/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/system/add_to_token_total_supply/v0/mod.rs @@ -18,11 +18,11 @@ impl Drive { pub(super) fn add_to_token_total_supply_v0( &self, token_id: [u8; 32], - amount: u64, + amount: TokenAmount, allow_first_mint: bool, allow_saturation: bool, - block_info: &BlockInfo, apply: bool, + block_info: &BlockInfo, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result<(FeeResult, TokenAmount), Error> { @@ -31,9 +31,9 @@ impl Drive { let token_amount = self.add_to_token_total_supply_add_to_operations_v0( token_id, amount, - apply, allow_first_mint, allow_saturation, + apply, transaction, &mut drive_operations, platform_version, diff --git a/packages/rs-drive/src/util/grove_operations/grove_apply_batch_with_add_costs/v0/mod.rs b/packages/rs-drive/src/util/grove_operations/grove_apply_batch_with_add_costs/v0/mod.rs index ac4f8d3e61a..3c212ba5440 100644 --- a/packages/rs-drive/src/util/grove_operations/grove_apply_batch_with_add_costs/v0/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/grove_apply_batch_with_add_costs/v0/mod.rs @@ -26,10 +26,10 @@ impl Drive { "batch is empty when trying to apply batch with add costs".to_string(), ))); } - if ops.operations.len() < 500 { - //no initialization - println!("batch {}", &ops); - } + // if ops.operations.len() < 500 { + // //no initialization + // println!("batch {}", &ops); + // } if self.config.batching_consistency_verification { let consistency_results = diff --git a/packages/rs-drive/src/util/operations/apply_batch_low_level_drive_operations/v0/mod.rs b/packages/rs-drive/src/util/operations/apply_batch_low_level_drive_operations/v0/mod.rs index 74c318ee83e..f487000a0f5 100644 --- a/packages/rs-drive/src/util/operations/apply_batch_low_level_drive_operations/v0/mod.rs +++ b/packages/rs-drive/src/util/operations/apply_batch_low_level_drive_operations/v0/mod.rs @@ -31,6 +31,8 @@ impl Drive { drive_operations, drive_version, )?; + } else { + println!("it was empty!"); } drive_operations.append(&mut other_operations); Ok(()) From a8e5d53b77bca829e9675a4c08ccbd3aeaef7eff Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sun, 16 Mar 2025 09:40:18 +0700 Subject: [PATCH 5/7] fix --- .../state_transitions/batch/tests/document/creation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs index df64cec4233..1e4f8f30322 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rs @@ -2146,7 +2146,7 @@ mod creation_tests { let PaidConsensusError(consensus_error, _) = result else { panic!("expected a paid consensus error"); }; - assert_eq!(consensus_error.to_string(), "An Identity with the id `BjNejy4r9QAvLHpQ9Yq6yRMgNymeGZ46d48fJxJbMrfW` is already a contestant for the vote_poll ContestedDocumentResourceVotePoll { contract_id: GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec, document_type_name: domain, index_name: parentNameAndLabel, index_values: [string dash, string quantum] }"); + assert_eq!(consensus_error.to_string(), "An Identity with the id BjNejy4r9QAvLHpQ9Yq6yRMgNymeGZ46d48fJxJbMrfW is already a contestant for the vote_poll ContestedDocumentResourceVotePoll { contract_id: GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec, document_type_name: domain, index_name: parentNameAndLabel, index_values: [string dash, string quantum] }"); } #[test] From 08f006dd4c21bc2c411222806df9ef274df53e0d Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 17 Mar 2025 16:08:59 +0700 Subject: [PATCH 6/7] fix clippy --- packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs | 1 + .../rs-dpp/src/data_contract/document_type/accessors/v0/mod.rs | 1 + packages/rs-dpp/src/data_contract/document_type/v0/accessors.rs | 1 + packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs b/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs index 02ed549858f..49facffa179 100644 --- a/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/accessors/mod.rs @@ -10,6 +10,7 @@ use platform_value::{Identifier, Value}; use crate::balances::credits::TokenAmount; use crate::data_contract::document_type::restricted_creation::CreationRestrictionMode; +#[cfg(feature = "validation")] use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; use crate::data_contract::TokenContractPosition; diff --git a/packages/rs-dpp/src/data_contract/document_type/accessors/v0/mod.rs b/packages/rs-dpp/src/data_contract/document_type/accessors/v0/mod.rs index fb696e7077e..5769860f1ae 100644 --- a/packages/rs-dpp/src/data_contract/document_type/accessors/v0/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/accessors/v0/mod.rs @@ -5,6 +5,7 @@ use crate::data_contract::document_type::property::DocumentProperty; use platform_value::{Identifier, Value}; use crate::data_contract::document_type::restricted_creation::CreationRestrictionMode; +#[cfg(feature = "validation")] use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; use crate::document::transfer::Transferable; diff --git a/packages/rs-dpp/src/data_contract/document_type/v0/accessors.rs b/packages/rs-dpp/src/data_contract/document_type/v0/accessors.rs index d2e23369ad2..f04b9fa39fe 100644 --- a/packages/rs-dpp/src/data_contract/document_type/v0/accessors.rs +++ b/packages/rs-dpp/src/data_contract/document_type/v0/accessors.rs @@ -9,6 +9,7 @@ use crate::data_contract::document_type::v0::DocumentTypeV0; use platform_value::{Identifier, Value}; use crate::data_contract::document_type::restricted_creation::CreationRestrictionMode; +#[cfg(feature = "validation")] use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; use crate::document::transfer::Transferable; diff --git a/packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs b/packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs index 85b2e1cbfbb..54caaa1fa8d 100644 --- a/packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs +++ b/packages/rs-dpp/src/data_contract/document_type/v1/accessors.rs @@ -11,6 +11,7 @@ use crate::balances::credits::TokenAmount; use crate::data_contract::document_type::restricted_creation::CreationRestrictionMode; use crate::data_contract::document_type::token_costs::accessors::TokenCostGettersV0; use crate::data_contract::document_type::v1::DocumentTypeV1; +#[cfg(feature = "validation")] use crate::data_contract::document_type::validator::StatelessJsonSchemaLazyValidator; use crate::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; use crate::data_contract::TokenContractPosition; From ecd1ea449a91626c8e535c523cc794aff734818f Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 17 Mar 2025 18:49:32 +0700 Subject: [PATCH 7/7] remove a println --- .../operations/apply_batch_low_level_drive_operations/v0/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/rs-drive/src/util/operations/apply_batch_low_level_drive_operations/v0/mod.rs b/packages/rs-drive/src/util/operations/apply_batch_low_level_drive_operations/v0/mod.rs index f487000a0f5..74c318ee83e 100644 --- a/packages/rs-drive/src/util/operations/apply_batch_low_level_drive_operations/v0/mod.rs +++ b/packages/rs-drive/src/util/operations/apply_batch_low_level_drive_operations/v0/mod.rs @@ -31,8 +31,6 @@ impl Drive { drive_operations, drive_version, )?; - } else { - println!("it was empty!"); } drive_operations.append(&mut other_operations); Ok(())