From d6be75918cfefa6c5ebba66152d072f51523c520 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 18 Jun 2024 16:04:08 +0300 Subject: [PATCH 1/3] fix: contested resources query fixes --- .../document_type/property/mod.rs | 2 +- .../state_transitions/masternode_vote/mod.rs | 273 ++++++++++++++++++ .../contract/insert/insert_contract/v0/mod.rs | 35 +-- .../v0/mod.rs | 5 +- packages/rs-drive/src/drive/document/mod.rs | 6 +- .../v0/mod.rs | 23 +- packages/rs-drive/src/drive/votes/paths.rs | 46 ++- .../mod.rs | 6 +- .../vote_polls_by_document_type_query.rs | 7 +- 9 files changed, 366 insertions(+), 37 deletions(-) diff --git a/packages/rs-dpp/src/data_contract/document_type/property/mod.rs b/packages/rs-dpp/src/data_contract/document_type/property/mod.rs index f881f37719..b3d8be77a8 100644 --- a/packages/rs-dpp/src/data_contract/document_type/property/mod.rs +++ b/packages/rs-dpp/src/data_contract/document_type/property/mod.rs @@ -850,7 +850,7 @@ impl DocumentPropertyType { Ok(Value::Text(String::from_utf8(value.to_vec()).map_err( |_| { ProtocolError::DecodingError( - "could not decode ut8 bytes into string".to_string(), + "could not decode utf8 bytes into string".to_string(), ) }, )?)) diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs index 4889eb5689..04c53d337f 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs @@ -369,6 +369,279 @@ mod tests { assert_eq!(contests.len(), 2); } + + #[test] + fn test_empty_string_start_index_value() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .build_with_mock_rpc() + .set_genesis_state(); + + let platform_state = platform.state.load(); + + let (_contender_1, _contender_2, dpns_contract) = create_dpns_name_contest( + &mut platform, + &platform_state, + 7, + "quantum", + platform_version, + ); + + let domain = dpns_contract + .document_type_for_name("domain") + .expect("expected a profile document type"); + + let index_name = "parentNameAndLabel".to_string(); + + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + + let empty_encoded = bincode::encode_to_vec(Value::Text("".to_string()), config) + .expect("expected to encode value"); + + { + let query_validation_result = platform + .query_contested_resources( + GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: domain.name().clone(), + index_name: index_name.clone(), + start_index_values: vec![empty_encoded.clone()], + end_index_values: vec![], + start_at_value_info: None, + count: None, + order_ascending: true, + prove: false, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resources_response::Version::V0( + GetContestedResourcesResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some(get_contested_resources_response_v0::Result::ContestedResourceValues( + get_contested_resources_response_v0::ContestedResourceValues { + contested_resource_values, + }, + )) = result + else { + panic!("expected contested resources") + }; + + assert_eq!(contested_resource_values.len(), 0); + } + + { + let query_validation_result = platform + .query_contested_resources( + GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: domain.name().clone(), + index_name: index_name.clone(), + start_index_values: vec![empty_encoded], + end_index_values: vec![], + start_at_value_info: None, + count: None, + order_ascending: true, + prove: true, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resources_response::Version::V0( + GetContestedResourcesResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some(get_contested_resources_response_v0::Result::Proof(proof)) = result + else { + panic!("expected proof") + }; + + let resolved_contested_document_vote_poll_drive_query = + ResolvedVotePollsByDocumentTypeQuery { + contract: DataContractResolvedInfo::BorrowedDataContract( + dpns_contract.as_ref(), + ), + document_type_name: domain.name(), + index_name: &index_name, + start_index_values: &vec!["".into()], + end_index_values: &vec![], + limit: None, + order_ascending: true, + start_at_value: &None, + }; + + let (_, contests) = resolved_contested_document_vote_poll_drive_query + .verify_contests_proof(proof.grovedb_proof.as_ref(), platform_version) + .expect("expected to verify proof"); + + assert_eq!(contests.len(), 0); + } + } + + #[test] + fn test_no_start_index_value() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .build_with_mock_rpc() + .set_genesis_state(); + + let platform_state = platform.state.load(); + + let (_contender_1, _contender_2, dpns_contract) = create_dpns_name_contest( + &mut platform, + &platform_state, + 7, + "quantum", + platform_version, + ); + + let domain = dpns_contract + .document_type_for_name("domain") + .expect("expected a profile document type"); + + let index_name = "parentNameAndLabel".to_string(); + + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + + { + let query_validation_result = platform + .query_contested_resources( + GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: domain.name().clone(), + index_name: index_name.clone(), + start_index_values: vec![], + end_index_values: vec![], + start_at_value_info: None, + count: None, + order_ascending: true, + prove: false, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resources_response::Version::V0( + GetContestedResourcesResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some(get_contested_resources_response_v0::Result::ContestedResourceValues( + get_contested_resources_response_v0::ContestedResourceValues { + contested_resource_values, + }, + )) = result + else { + panic!("expected contested resources") + }; + + let dash_encoded = + bincode::encode_to_vec(Value::Text("dash".to_string()), config) + .expect("expected to encode the word dash"); + + assert_eq!( + contested_resource_values.first(), + Some(dash_encoded).as_ref() + ); + } + + { + let query_validation_result = platform + .query_contested_resources( + GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: domain.name().clone(), + index_name: index_name.clone(), + start_index_values: vec![], + end_index_values: vec![], + start_at_value_info: None, + count: None, + order_ascending: true, + prove: true, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resources_response::Version::V0( + GetContestedResourcesResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some(get_contested_resources_response_v0::Result::Proof(proof)) = result + else { + panic!("expected proof") + }; + + let resolved_contested_document_vote_poll_drive_query = + ResolvedVotePollsByDocumentTypeQuery { + contract: DataContractResolvedInfo::BorrowedDataContract( + dpns_contract.as_ref(), + ), + document_type_name: domain.name(), + index_name: &index_name, + start_index_values: &vec![], + end_index_values: &vec![], + limit: None, + order_ascending: true, + start_at_value: &None, + }; + + let (_, contests) = resolved_contested_document_vote_poll_drive_query + .verify_contests_proof(proof.grovedb_proof.as_ref(), platform_version) + .expect("expected to verify proof"); + + assert_eq!( + contests.first(), + Some(Value::Text("dash".to_string())).as_ref() + ); + } + } } mod vote_state_query { 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 11876d174f..d4d5f7fae1 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 @@ -15,6 +15,9 @@ use dpp::fee::fee_result::FeeResult; use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; use dpp::serialization::PlatformSerializableWithPlatformVersion; +use crate::drive::votes::paths::{ + CONTESTED_DOCUMENT_INDEXES_TREE_KEY, CONTESTED_DOCUMENT_STORAGE_TREE_KEY, +}; use crate::error::contract::DataContractError; use dpp::version::PlatformVersion; use grovedb::batch::KeyInfoPath; @@ -236,34 +239,24 @@ impl Drive { ]; // primary key tree - let key_info = Key(vec![0]); + let key_info_storage = Key(vec![CONTESTED_DOCUMENT_STORAGE_TREE_KEY]); self.batch_insert_empty_tree( type_path, - key_info, + key_info_storage, storage_flags.as_ref(), &mut batch_operations, &platform_version.drive, )?; - let mut index_cache: HashSet<&[u8]> = HashSet::new(); - // for each type we should insert the indices that are top level - for index in document_type - .as_ref() - .top_level_indices_of_contested_unique_indexes() - { - // toDo: change this to be a reference by index - let index_bytes = index.name.as_bytes(); - if !index_cache.contains(index_bytes) { - self.batch_insert_empty_tree( - type_path, - KeyRef(index_bytes), - storage_flags.as_ref(), - &mut batch_operations, - &platform_version.drive, - )?; - index_cache.insert(index_bytes); - } - } + // index key tree + let key_info_indexes = Key(vec![CONTESTED_DOCUMENT_INDEXES_TREE_KEY]); + self.batch_insert_empty_tree( + type_path, + key_info_indexes, + storage_flags.as_ref(), + &mut batch_operations, + &platform_version.drive, + )?; } } diff --git a/packages/rs-drive/src/drive/document/insert_contested/add_contested_indices_for_contract_operations/v0/mod.rs b/packages/rs-drive/src/drive/document/insert_contested/add_contested_indices_for_contract_operations/v0/mod.rs index 0c4c4cb542..cae6338576 100644 --- a/packages/rs-drive/src/drive/document/insert_contested/add_contested_indices_for_contract_operations/v0/mod.rs +++ b/packages/rs-drive/src/drive/document/insert_contested/add_contested_indices_for_contract_operations/v0/mod.rs @@ -14,7 +14,8 @@ use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use crate::drive::defaults::DEFAULT_HASH_SIZE_U8; use crate::drive::votes::paths::{ vote_contested_resource_active_polls_contract_document_tree_path_vec, - RESOURCE_ABSTAIN_VOTE_TREE_KEY, RESOURCE_LOCK_VOTE_TREE_KEY, + vote_contested_resource_contract_documents_indexes_path_vec, RESOURCE_ABSTAIN_VOTE_TREE_KEY, + RESOURCE_LOCK_VOTE_TREE_KEY, }; use crate::error::drive::DriveError; use dpp::data_contract::document_type::IndexProperty; @@ -65,7 +66,7 @@ impl Drive { // * DataContract ID recovered from document // * 0 to signify Documents and notDataContract let contract_document_type_path = - vote_contested_resource_active_polls_contract_document_tree_path_vec( + vote_contested_resource_contract_documents_indexes_path_vec( document_and_contract_info.contract.id_ref().as_bytes(), document_and_contract_info.document_type.name(), ); diff --git a/packages/rs-drive/src/drive/document/mod.rs b/packages/rs-drive/src/drive/document/mod.rs index aee30c8a9c..3221f766c8 100644 --- a/packages/rs-drive/src/drive/document/mod.rs +++ b/packages/rs-drive/src/drive/document/mod.rs @@ -6,6 +6,7 @@ #[cfg(feature = "server")] use crate::drive::flags::StorageFlags; +use crate::drive::votes::paths::CONTESTED_DOCUMENT_STORAGE_TREE_KEY; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; #[cfg(any(feature = "server", feature = "verify"))] use dpp::data_contract::document_type::DocumentTypeRef; @@ -82,7 +83,10 @@ fn make_document_contested_reference( // 0 represents document storage // Then we add document id // Then we add 0 if the document type keys history - let reference_path = vec![vec![0], document.id().to_vec()]; + let reference_path = vec![ + vec![CONTESTED_DOCUMENT_STORAGE_TREE_KEY], + document.id().to_vec(), + ]; let max_reference_hops = 1; // 2 because the contract could allow for history // 5 because diff --git a/packages/rs-drive/src/drive/shared_estimation_costs/add_estimation_costs_for_contested_document_tree_levels_up_to_contract/v0/mod.rs b/packages/rs-drive/src/drive/shared_estimation_costs/add_estimation_costs_for_contested_document_tree_levels_up_to_contract/v0/mod.rs index f5fd811e43..918d91ff55 100644 --- a/packages/rs-drive/src/drive/shared_estimation_costs/add_estimation_costs_for_contested_document_tree_levels_up_to_contract/v0/mod.rs +++ b/packages/rs-drive/src/drive/shared_estimation_costs/add_estimation_costs_for_contested_document_tree_levels_up_to_contract/v0/mod.rs @@ -13,7 +13,8 @@ use grovedb::EstimatedLayerSizes::AllSubtrees; use crate::drive::votes::paths::{ vote_contested_resource_active_polls_contract_document_tree_path, vote_contested_resource_active_polls_contract_tree_path, - vote_contested_resource_active_polls_tree_path, vote_contested_resource_tree_path, + vote_contested_resource_active_polls_tree_path, + vote_contested_resource_contract_documents_indexes_path, vote_contested_resource_tree_path, vote_root_path, }; use dpp::data_contract::accessors::v0::DataContractV0Getters; @@ -130,7 +131,25 @@ impl Drive { ), EstimatedLayerInformation { is_sum_tree: false, - estimated_layer_count: ApproximateElements(document_type_count), + estimated_layer_count: ApproximateElements(2), + estimated_layer_sizes: AllSubtrees( + ESTIMATED_AVERAGE_INDEX_NAME_SIZE, + NoSumTrees, + None, + ), + }, + ); + + estimated_costs_only_with_layer_info.insert( + KeyInfoPath::from_known_path( + vote_contested_resource_contract_documents_indexes_path( + contract.id_ref().as_bytes(), + document_type.name().as_str(), + ), + ), + EstimatedLayerInformation { + is_sum_tree: false, + estimated_layer_count: ApproximateElements(1024), //Just a guess estimated_layer_sizes: AllSubtrees( ESTIMATED_AVERAGE_INDEX_NAME_SIZE, NoSumTrees, diff --git a/packages/rs-drive/src/drive/votes/paths.rs b/packages/rs-drive/src/drive/votes/paths.rs index 2a107e1474..376a34fbb6 100644 --- a/packages/rs-drive/src/drive/votes/paths.rs +++ b/packages/rs-drive/src/drive/votes/paths.rs @@ -58,6 +58,12 @@ pub const RESOURCE_STORED_INFO_KEY: char = 'b'; // 62 hex 98 decimal /// The finished info pub const RESOURCE_STORED_INFO_KEY_U8: u8 = b'b'; // 62 hex 98 decimal +/// The tree key for storage of contested documents +pub const CONTESTED_DOCUMENT_STORAGE_TREE_KEY: u8 = 0; + +/// The tree key for the indexes of contested documents +pub const CONTESTED_DOCUMENT_INDEXES_TREE_KEY: u8 = 1; + /// The tree key for storage pub const VOTING_STORAGE_TREE_KEY: u8 = 1; @@ -141,7 +147,7 @@ impl VotePollPaths for ContestedDocumentResourceVotePollWithContractInfo { } fn contenders_path(&self, platform_version: &PlatformVersion) -> Result>, Error> { - let mut root = vote_contested_resource_active_polls_contract_document_tree_path_vec( + let mut root = vote_contested_resource_contract_documents_indexes_path_vec( self.contract.as_ref().id_ref().as_slice(), self.document_type_name.as_str(), ); @@ -227,7 +233,7 @@ impl<'a> VotePollPaths for ContestedDocumentResourceVotePollWithContractInfoAllo } fn contenders_path(&self, platform_version: &PlatformVersion) -> Result>, Error> { - let mut root = vote_contested_resource_active_polls_contract_document_tree_path_vec( + let mut root = vote_contested_resource_contract_documents_indexes_path_vec( self.contract.as_ref().id_ref().as_slice(), self.document_type_name.as_str(), ); @@ -266,7 +272,7 @@ impl<'a> VotePollPaths for ContestedDocumentResourceVotePollWithContractInfoAllo let key = vote_choice.to_key(); let mut contender_voting_path = self.contenders_path(platform_version)?; contender_voting_path.push(key); - contender_voting_path.push(vec![1]); + contender_voting_path.push(vec![VOTING_STORAGE_TREE_KEY]); Ok(contender_voting_path) } } @@ -408,7 +414,7 @@ pub fn vote_contested_resource_contract_documents_storage_path<'a>( &[ACTIVE_POLLS_TREE_KEY as u8], // 1 contract_id, // 32 document_type_name.as_bytes(), - &[0], // 1 + &[CONTESTED_DOCUMENT_STORAGE_TREE_KEY], // 1 ] } @@ -423,7 +429,37 @@ pub fn vote_contested_resource_contract_documents_storage_path_vec( vec![ACTIVE_POLLS_TREE_KEY as u8], contract_id.to_vec(), document_type_name.as_bytes().to_vec(), - vec![0], + vec![CONTESTED_DOCUMENT_STORAGE_TREE_KEY], + ] +} + +/// Returns the path to the primary keys of a contract document type. +pub fn vote_contested_resource_contract_documents_indexes_path<'a>( + contract_id: &'a [u8], + document_type_name: &'a str, +) -> [&'a [u8]; 6] { + [ + Into::<&[u8; 1]>::into(RootTree::Votes), // 1 + &[CONTESTED_RESOURCE_TREE_KEY as u8], // 1 + &[ACTIVE_POLLS_TREE_KEY as u8], // 1 + contract_id, // 32 + document_type_name.as_bytes(), + &[CONTESTED_DOCUMENT_INDEXES_TREE_KEY], // 1 + ] +} + +/// Returns the path to the primary keys of a contract document type as a vec. +pub fn vote_contested_resource_contract_documents_indexes_path_vec( + contract_id: &[u8], + document_type_name: &str, +) -> Vec> { + vec![ + vec![RootTree::Votes as u8], + vec![CONTESTED_RESOURCE_TREE_KEY as u8], + vec![ACTIVE_POLLS_TREE_KEY as u8], + contract_id.to_vec(), + document_type_name.as_bytes().to_vec(), + vec![CONTESTED_DOCUMENT_INDEXES_TREE_KEY], ] } diff --git a/packages/rs-drive/src/drive/votes/storage_form/contested_document_resource_storage_form/mod.rs b/packages/rs-drive/src/drive/votes/storage_form/contested_document_resource_storage_form/mod.rs index 9654e7a5e5..7d325fac50 100644 --- a/packages/rs-drive/src/drive/votes/storage_form/contested_document_resource_storage_form/mod.rs +++ b/packages/rs-drive/src/drive/votes/storage_form/contested_document_resource_storage_form/mod.rs @@ -113,7 +113,7 @@ impl TreePathStorageForm for ContestedDocumentResourceVoteStorageForm { where Self: Sized, { - if path.len() < 9 { + if path.len() < 10 { return Err(ProtocolError::VoteError(format!( "path {} is not long enough to construct vote information", path.into_iter() @@ -174,8 +174,8 @@ impl TreePathStorageForm for ContestedDocumentResourceVoteStorageForm { return Err(ProtocolError::VoteError(format!("path {} 2 before last element must be an identifier or RESOURCE_ABSTAIN_VOTE_TREE_KEY/RESOURCE_LOCK_VOTE_TREE_KEY", path.into_iter().map(hex::encode).collect::>().join("/")))); }; - // 5 is the first index value, then we have 2 at the end that are not index values - let index_values = path.drain(5..path.len() - 3).collect::>(); + // 6 is the first index value, then we have 2 at the end that are not index values + let index_values = path.drain(6..path.len() - 3).collect::>(); Ok(ContestedDocumentResourceVoteStorageForm { contract_id, diff --git a/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs b/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs index 8040288a5b..c22f3a7d89 100644 --- a/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs +++ b/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs @@ -1,6 +1,9 @@ use super::ContractLookupFn; use crate::drive::object_size_info::DataContractResolvedInfo; -use crate::drive::votes::paths::vote_contested_resource_active_polls_contract_document_tree_path_vec; +use crate::drive::votes::paths::{ + vote_contested_resource_active_polls_contract_document_tree_path_vec, + vote_contested_resource_contract_documents_indexes_path_vec, +}; #[cfg(feature = "server")] use crate::drive::Drive; use crate::error::contract::DataContractError; @@ -375,7 +378,7 @@ impl<'a> ResolvedVotePollsByDocumentTypeQuery<'a> { index: &Index, platform_version: &PlatformVersion, ) -> Result { - let mut path = vote_contested_resource_active_polls_contract_document_tree_path_vec( + let mut path = vote_contested_resource_contract_documents_indexes_path_vec( self.contract.id().as_ref(), self.document_type_name, ); From 4e77ddd8b0c32890b86c4acb4d79869b420a6eb0 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 18 Jun 2024 16:15:37 +0300 Subject: [PATCH 2/3] limit test causes issue --- .../state_transitions/masternode_vote/mod.rs | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs index 04c53d337f..af5734db86 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs @@ -642,6 +642,156 @@ mod tests { ); } } + + #[test] + #[ignore] // Currently will have an issue due to a grovedb bug + fn test_limit() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .build_with_mock_rpc() + .set_genesis_state(); + + let platform_state = platform.state.load(); + + let (_contender_1, _contender_2, dpns_contract) = create_dpns_name_contest( + &mut platform, + &platform_state, + 7, + "quantum", + platform_version, + ); + + let (_contender_3, _contender_4, dpns_contract) = create_dpns_name_contest( + &mut platform, + &platform_state, + 8, + "coya", + platform_version, + ); + + let (_contender_5, _contender_6, dpns_contract) = create_dpns_name_contest( + &mut platform, + &platform_state, + 9, + "tobe", + platform_version, + ); + + let domain = dpns_contract + .document_type_for_name("domain") + .expect("expected a profile document type"); + + let index_name = "parentNameAndLabel".to_string(); + + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + + let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config) + .expect("expected to encode value"); + + { + let query_validation_result = platform + .query_contested_resources( + GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: domain.name().clone(), + index_name: index_name.clone(), + start_index_values: vec![dash_encoded.clone()], + end_index_values: vec![], + start_at_value_info: None, + count: Some(2), + order_ascending: true, + prove: false, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resources_response::Version::V0( + GetContestedResourcesResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some(get_contested_resources_response_v0::Result::ContestedResourceValues( + get_contested_resources_response_v0::ContestedResourceValues { + contested_resource_values, + }, + )) = result + else { + panic!("expected contested resources") + }; + + assert_eq!(contested_resource_values.len(), 2); + } + + { + let query_validation_result = platform + .query_contested_resources( + GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: domain.name().clone(), + index_name: index_name.clone(), + start_index_values: vec![dash_encoded], + end_index_values: vec![], + start_at_value_info: None, + count: Some(2), + order_ascending: true, + prove: true, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resources_response::Version::V0( + GetContestedResourcesResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some(get_contested_resources_response_v0::Result::Proof(proof)) = result + else { + panic!("expected proof") + }; + + let resolved_contested_document_vote_poll_drive_query = + ResolvedVotePollsByDocumentTypeQuery { + contract: DataContractResolvedInfo::BorrowedDataContract( + dpns_contract.as_ref(), + ), + document_type_name: domain.name(), + index_name: &index_name, + start_index_values: &vec!["dash".into()], + end_index_values: &vec![], + limit: Some(2), + order_ascending: true, + start_at_value: &None, + }; + + let (_, contests) = resolved_contested_document_vote_poll_drive_query + .verify_contests_proof(proof.grovedb_proof.as_ref(), platform_version) + .expect("expected to verify proof"); + + assert_eq!(contests.len(), 2); + } + } } mod vote_state_query { From 1184899a8904db4ab31a3ba2657956eb381be9c2 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 18 Jun 2024 17:10:31 +0300 Subject: [PATCH 3/3] fixed start_at --- .../state_transitions/masternode_vote/mod.rs | 170 +++++++++++++++++- .../voting/contested_resources/v0/mod.rs | 33 +++- .../vote_polls_by_document_type_query.rs | 45 ++--- 3 files changed, 220 insertions(+), 28 deletions(-) diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs index af5734db86..94d7091ac7 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs @@ -172,8 +172,8 @@ mod tests { use super::*; mod contests_requests_query { - use super::*; + use dapi_grpc::platform::v0::get_contested_resources_request::get_contested_resources_request_v0; #[test] fn test_not_proved_contests_request() { @@ -792,6 +792,174 @@ mod tests { assert_eq!(contests.len(), 2); } } + + #[test] + fn test_start_at() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .build_with_mock_rpc() + .set_genesis_state(); + + let platform_state = platform.state.load(); + + let (_contender_1, _contender_2, dpns_contract) = create_dpns_name_contest( + &mut platform, + &platform_state, + 7, + "quantum", + platform_version, + ); + + let (_contender_3, _contender_4, dpns_contract) = create_dpns_name_contest( + &mut platform, + &platform_state, + 8, + "coya", + platform_version, + ); + + let (_contender_5, _contender_6, dpns_contract) = create_dpns_name_contest( + &mut platform, + &platform_state, + 9, + "tobe", + platform_version, + ); + + let domain = dpns_contract + .document_type_for_name("domain") + .expect("expected a profile document type"); + + let index_name = "parentNameAndLabel".to_string(); + + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + + let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config) + .expect("expected to encode value"); + + let quantum_encoded = bincode::encode_to_vec( + Value::Text(convert_to_homograph_safe_chars("quantum")), + config, + ) + .expect("expected to encode value"); + + { + let query_validation_result = platform + .query_contested_resources( + GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: domain.name().clone(), + index_name: index_name.clone(), + start_index_values: vec![dash_encoded.clone()], + end_index_values: vec![], + start_at_value_info: Some( + get_contested_resources_request_v0::StartAtValueInfo { + start_value: quantum_encoded.clone(), + start_value_included: false, + }, + ), + count: None, + order_ascending: true, + prove: false, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resources_response::Version::V0( + GetContestedResourcesResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some(get_contested_resources_response_v0::Result::ContestedResourceValues( + get_contested_resources_response_v0::ContestedResourceValues { + contested_resource_values, + }, + )) = result + else { + panic!("expected contested resources") + }; + + assert_eq!(contested_resource_values.len(), 1); + } + + { + let query_validation_result = platform + .query_contested_resources( + GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: domain.name().clone(), + index_name: index_name.clone(), + start_index_values: vec![dash_encoded], + end_index_values: vec![], + start_at_value_info: Some( + get_contested_resources_request_v0::StartAtValueInfo { + start_value: quantum_encoded.clone(), + start_value_included: false, + }, + ), + count: None, + order_ascending: true, + prove: true, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resources_response::Version::V0( + GetContestedResourcesResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some(get_contested_resources_response_v0::Result::Proof(proof)) = result + else { + panic!("expected proof") + }; + + let resolved_contested_document_vote_poll_drive_query = + ResolvedVotePollsByDocumentTypeQuery { + contract: DataContractResolvedInfo::BorrowedDataContract( + dpns_contract.as_ref(), + ), + document_type_name: domain.name(), + index_name: &index_name, + start_index_values: &vec!["dash".into()], + end_index_values: &vec![], + limit: None, + order_ascending: true, + start_at_value: &Some(( + Value::Text(convert_to_homograph_safe_chars("quantum")), + false, + )), + }; + + let (_, contests) = resolved_contested_document_vote_poll_drive_query + .verify_contests_proof(proof.grovedb_proof.as_ref(), platform_version) + .expect("expected to verify proof"); + + assert_eq!(contests.len(), 1); + } + } } mod vote_state_query { diff --git a/packages/rs-drive-abci/src/query/voting/contested_resources/v0/mod.rs b/packages/rs-drive-abci/src/query/voting/contested_resources/v0/mod.rs index 656fc78be1..48315a868a 100644 --- a/packages/rs-drive-abci/src/query/voting/contested_resources/v0/mod.rs +++ b/packages/rs-drive-abci/src/query/voting/contested_resources/v0/mod.rs @@ -142,18 +142,39 @@ impl Platform { format!("limit greater than max limit {}", config.max_query_limit), )))?; + let result = start_at_value_info + .map(|start_at_value_info| { + let start = bincode::decode_from_slice( + start_at_value_info.start_value.as_slice(), + bincode_config, + ) + .map_err(|_| { + QueryError::InvalidArgument(format!( + "could not convert {:?} to a value for start at", + start_at_value_info.start_value + )) + })? + .0; + + Ok::<(dpp::platform_value::Value, bool), QueryError>(( + start, + start_at_value_info.start_value_included, + )) + }) + .transpose(); + + let start_at_value_info = match result { + Ok(start_at_value_info) => start_at_value_info, + Err(e) => return Ok(QueryValidationResult::new_with_error(e)), + }; + let query = VotePollsByDocumentTypeQuery { contract_id, document_type_name, index_name, start_index_values, end_index_values, - start_at_value: start_at_value_info.map(|start_at_value_info| { - ( - start_at_value_info.start_value, - start_at_value_info.start_value_included, - ) - }), + start_at_value: start_at_value_info, limit: Some(limit), order_ascending, }; diff --git a/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs b/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs index c22f3a7d89..104f954b97 100644 --- a/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs +++ b/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs @@ -48,7 +48,7 @@ pub struct VotePollsByDocumentTypeQuery { /// All values that are after the missing property number pub end_index_values: Vec, /// Start at value - pub start_at_value: Option<(Vec, bool)>, + pub start_at_value: Option<(Value, bool)>, /// Limit pub limit: Option, /// Ascending @@ -69,7 +69,7 @@ pub struct ResolvedVotePollsByDocumentTypeQuery<'a> { /// All values that are after the missing property number pub end_index_values: &'a Vec, /// Start at value - pub start_at_value: &'a Option<(Vec, bool)>, + pub start_at_value: &'a Option<(Value, bool)>, /// Limit pub limit: Option, /// Ascending @@ -290,11 +290,11 @@ impl<'a> ResolvedVotePollsByDocumentTypeQuery<'a> { } /// Creates the vectors of indexes - fn indexes_vectors( + fn indexes_vectors<'b>( &self, - index: &Index, + index: &'b Index, platform_version: &PlatformVersion, - ) -> Result<(Vec>, Vec>), Error> { + ) -> Result<(Vec>, Vec>, &'b IndexProperty), Error> { let document_type = self.document_type()?; let properties_iter = index.properties.iter(); let mut start_values_iter = self.start_index_values.iter(); @@ -302,7 +302,7 @@ impl<'a> ResolvedVotePollsByDocumentTypeQuery<'a> { let mut start_values_vec = vec![]; let mut end_values_vec = vec![]; let mut ended_start_values = false; - let mut started_end_values = false; + let mut middle_index_property = None; for index_property in properties_iter { if !ended_start_values { if let Some(start_value) = start_values_iter.next() { @@ -314,23 +314,21 @@ impl<'a> ResolvedVotePollsByDocumentTypeQuery<'a> { start_values_vec.push(encoded); } else { ended_start_values = true; + middle_index_property = Some(index_property); } - } else if started_end_values { - if let Some(end_value) = end_values_iter.next() { - let encoded = document_type.serialize_value_for_key( - &index_property.name, - end_value, - platform_version, - )?; - end_values_vec.push(encoded); - } else { - return Err(Error::Query(QuerySyntaxError::IndexValuesError("the start index values and the end index values must be equal to the amount of properties in the contested index minus one".to_string()))); - } + } else if let Some(end_value) = end_values_iter.next() { + let encoded = document_type.serialize_value_for_key( + &index_property.name, + end_value, + platform_version, + )?; + end_values_vec.push(encoded); } else { - started_end_values = true; + break; } } - Ok((start_values_vec, end_values_vec)) + let middle_index_property = middle_index_property.ok_or(Error::Query(QuerySyntaxError::IndexValuesError("the start index values and the end index values must be equal to the amount of properties in the contested index minus one, no middle property".to_string())))?; + Ok((start_values_vec, end_values_vec, middle_index_property)) } pub(crate) fn property_name_being_searched( @@ -383,7 +381,7 @@ impl<'a> ResolvedVotePollsByDocumentTypeQuery<'a> { self.document_type_name, ); - let (mut start, end) = self.indexes_vectors(index, platform_version)?; + let (mut start, end, middle_property) = self.indexes_vectors(index, platform_version)?; if !start.is_empty() { path.append(&mut start); @@ -397,7 +395,12 @@ impl<'a> ResolvedVotePollsByDocumentTypeQuery<'a> { query.insert_all(); } Some((starts_at_key_bytes, start_at_included)) => { - let starts_at_key = starts_at_key_bytes.to_vec(); + let starts_at_key = self.document_type()?.serialize_value_for_key( + &middle_property.name, + starts_at_key_bytes, + platform_version, + )?; + match self.order_ascending { true => match start_at_included { true => query.insert_range_from(starts_at_key..),