diff --git a/packages/dapi-grpc/protos/platform/v0/platform.proto b/packages/dapi-grpc/protos/platform/v0/platform.proto index b46d784ceb..8892d972ee 100644 --- a/packages/dapi-grpc/protos/platform/v0/platform.proto +++ b/packages/dapi-grpc/protos/platform/v0/platform.proto @@ -33,6 +33,8 @@ service Platform { rpc getDocuments(GetDocumentsRequest) returns (GetDocumentsResponse); rpc getIdentityByPublicKeyHash(GetIdentityByPublicKeyHashRequest) returns (GetIdentityByPublicKeyHashResponse); + rpc getIdentitiesForNonUniquePublicKeyHash(GetIdentitiesForNonUniquePublicKeyHashRequest) + returns (GetIdentitiesForNonUniquePublicKeyHashResponse); rpc waitForStateTransitionResult(WaitForStateTransitionResultRequest) returns (WaitForStateTransitionResultResponse); rpc getConsensusParams(GetConsensusParamsRequest) @@ -592,6 +594,31 @@ message GetIdentityByPublicKeyHashResponse { oneof version { GetIdentityByPublicKeyHashResponseV0 v0 = 1; } } +message GetIdentitiesForNonUniquePublicKeyHashRequest { + message GetIdentitiesForNonUniquePublicKeyHashRequestV0 { + bytes public_key_hash = 1; // The non-unique public key hash for which identities are requested + bool prove = 2; // Flag to request proof for the response + } + oneof version { GetIdentitiesForNonUniquePublicKeyHashRequestV0 v0 = 1; } +} + +message GetIdentitiesForNonUniquePublicKeyHashResponse { + message GetIdentitiesForNonUniquePublicKeyHashResponseV0 { + message Identities { + repeated bytes identities = 1; + } + + oneof result { + Identities identities = 1; // The list of identity data corresponding to the requested public key hash + Proof proof = 2; // Cryptographic proof for the identities data, if requested + } + + ResponseMetadata metadata = 3; // Metadata about the blockchain state + } + oneof version { GetIdentitiesForNonUniquePublicKeyHashResponseV0 v0 = 1; } +} + + message WaitForStateTransitionResultRequest { message WaitForStateTransitionResultRequestV0 { bytes state_transition_hash = 1; // The hash of the state transition to wait for diff --git a/packages/rs-drive-abci/src/query/identity_based_queries/identities_for_non_unique_public_key_hash/mod.rs b/packages/rs-drive-abci/src/query/identity_based_queries/identities_for_non_unique_public_key_hash/mod.rs new file mode 100644 index 0000000000..91f84bed57 --- /dev/null +++ b/packages/rs-drive-abci/src/query/identity_based_queries/identities_for_non_unique_public_key_hash/mod.rs @@ -0,0 +1,70 @@ +use crate::error::query::QueryError; +use crate::error::Error; +use crate::platform_types::platform::Platform; +use crate::platform_types::platform_state::PlatformState; +use crate::query::QueryValidationResult; +use dapi_grpc::platform::v0::get_identities_for_non_unique_public_key_hash_request::Version as RequestVersion; +use dapi_grpc::platform::v0::get_identities_for_non_unique_public_key_hash_response::Version as ResponseVersion; +use dapi_grpc::platform::v0::{ + GetIdentitiesForNonUniquePublicKeyHashRequest, GetIdentitiesForNonUniquePublicKeyHashResponse, + GetIdentityByPublicKeyHashResponse, +}; +use dpp::version::PlatformVersion; + +mod v0; + +impl Platform { + /// Querying of identities for a non-unique public key hash + pub fn query_identities_for_non_unique_public_key_hash( + &self, + GetIdentitiesForNonUniquePublicKeyHashRequest { version }: GetIdentitiesForNonUniquePublicKeyHashRequest, + platform_state: &PlatformState, + platform_version: &PlatformVersion, + ) -> Result, Error> { + let Some(version) = version else { + return Ok(QueryValidationResult::new_with_error( + QueryError::DecodingError( + "could not decode identities for non unique public key hash query".to_string(), + ), + )); + }; + + let feature_version_bounds = &platform_version + .drive_abci + .query + .identity_based_queries + .identities_for_non_unique_public_key_hash; + + let feature_version = match &version { + RequestVersion::V0(_) => 0, + }; + + if !feature_version_bounds.check_version(feature_version) { + return Ok(QueryValidationResult::new_with_error( + QueryError::UnsupportedQueryVersion( + "identities_for_non_unique_public_key_hash".to_string(), + feature_version_bounds.min_version, + feature_version_bounds.max_version, + platform_version.protocol_version, + feature_version, + ), + )); + } + + match version { + RequestVersion::V0(request_v0) => { + let request = self.query_identities_for_non_unique_public_key_hash_v0( + request_v0, + platform_state, + platform_version, + )?; + + Ok(request.map( + |response_v0| GetIdentitiesForNonUniquePublicKeyHashResponse { + version: Some(ResponseVersion::V0(response_v0)), + }, + )) + } + } + } +} diff --git a/packages/rs-drive-abci/src/query/identity_based_queries/identities_for_non_unique_public_key_hash/v0/mod.rs b/packages/rs-drive-abci/src/query/identity_based_queries/identities_for_non_unique_public_key_hash/v0/mod.rs new file mode 100644 index 0000000000..e9cec9a212 --- /dev/null +++ b/packages/rs-drive-abci/src/query/identity_based_queries/identities_for_non_unique_public_key_hash/v0/mod.rs @@ -0,0 +1,152 @@ +use crate::error::query::QueryError; +use crate::error::Error; +use crate::platform_types::platform::Platform; +use crate::platform_types::platform_state::PlatformState; +use crate::query::QueryValidationResult; +use dapi_grpc::platform::v0::get_identities_for_non_unique_public_key_hash_request::GetIdentitiesForNonUniquePublicKeyHashRequestV0; +use dapi_grpc::platform::v0::get_identities_for_non_unique_public_key_hash_response::{ + get_identities_for_non_unique_public_key_hash_response_v0, + GetIdentitiesForNonUniquePublicKeyHashResponseV0, +}; +use dpp::check_validation_result_with_data; +use dpp::platform_value::Bytes20; +use dpp::serialization::PlatformSerializable; +use dpp::validation::ValidationResult; +use dpp::version::PlatformVersion; + +impl Platform { + pub(super) fn query_identities_for_non_unique_public_key_hash_v0( + &self, + GetIdentitiesForNonUniquePublicKeyHashRequestV0 { + public_key_hash, + prove, + }: GetIdentitiesForNonUniquePublicKeyHashRequestV0, + platform_state: &PlatformState, + platform_version: &PlatformVersion, + ) -> Result, Error> + { + let public_key_hash = + check_validation_result_with_data!(Bytes20::from_vec(public_key_hash) + .map(|bytes| bytes.0) + .map_err(|_| QueryError::InvalidArgument( + "public key hash must be 20 bytes long".to_string() + ))); + + let response = if prove { + let proof = self + .drive + .prove_full_identities_for_non_unique_public_key_hash( + public_key_hash, + None, + None, + platform_version, + )?; + + GetIdentitiesForNonUniquePublicKeyHashResponseV0 { + result: Some( + get_identities_for_non_unique_public_key_hash_response_v0::Result::Proof( + self.response_proof_v0(platform_state, proof), + ), + ), + metadata: Some(self.response_metadata_v0(platform_state)), + } + } else { + let identities = self + .drive + .fetch_full_identities_for_non_unique_public_key_hash( + public_key_hash, + None, + None, + platform_version, + )? + .into_iter() + .map(|identity| { + identity + .serialize_consume_to_bytes() + .map_err(Error::Protocol) + }) + .collect::, Error>>()?; + + GetIdentitiesForNonUniquePublicKeyHashResponseV0 { + metadata: Some(self.response_metadata_v0(platform_state)), + result: Some( + get_identities_for_non_unique_public_key_hash_response_v0::Result::Identities( + get_identities_for_non_unique_public_key_hash_response_v0::Identities { + identities, + }, + ), + ), + } + }; + + Ok(QueryValidationResult::new_with_data(response)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::query::tests::setup_platform; + use dpp::dashcore::Network; + + #[test] + fn test_invalid_public_key_hash() { + let (platform, state, version) = setup_platform(None, Network::Testnet, None); + + let request = GetIdentitiesForNonUniquePublicKeyHashRequestV0 { + public_key_hash: vec![0; 8], + prove: false, + }; + + let result = platform + .query_identities_for_non_unique_public_key_hash_v0(request, &state, version) + .expect("expected query to succeed"); + + assert!(matches!( + result.errors.as_slice(), + [QueryError::InvalidArgument(msg)] if msg == &"public key hash must be 20 bytes long".to_string() + )); + } + + #[test] + fn test_identity_not_found() { + let (platform, state, version) = setup_platform(None, Network::Testnet, None); + + let public_key_hash = vec![0; 20]; + let request = GetIdentitiesForNonUniquePublicKeyHashRequestV0 { + public_key_hash: public_key_hash.clone(), + prove: false, + }; + + let result = platform + .query_identities_for_non_unique_public_key_hash_v0(request, &state, version) + .expect("expected query to succeed"); + + assert!(result.errors.is_empty()); + } + + #[test] + fn test_identity_absence_proof() { + let (platform, state, version) = setup_platform(None, Network::Testnet, None); + + let public_key_hash = vec![0; 20]; + let request = GetIdentitiesForNonUniquePublicKeyHashRequestV0 { + public_key_hash: public_key_hash.clone(), + prove: true, + }; + + let result = platform + .query_identities_for_non_unique_public_key_hash_v0(request, &state, version) + .expect("expected query to succeed"); + + assert!(matches!( + result.data, + Some(GetIdentitiesForNonUniquePublicKeyHashResponseV0 { + result: Some( + get_identities_for_non_unique_public_key_hash_response_v0::Result::Proof(_) + ), + metadata: Some(_), + }) + )); + } +} diff --git a/packages/rs-drive-abci/src/query/identity_based_queries/mod.rs b/packages/rs-drive-abci/src/query/identity_based_queries/mod.rs index 29fea76521..0aeda1f98b 100644 --- a/packages/rs-drive-abci/src/query/identity_based_queries/mod.rs +++ b/packages/rs-drive-abci/src/query/identity_based_queries/mod.rs @@ -2,6 +2,7 @@ mod balance; mod balance_and_revision; mod balances; mod identities_contract_keys; +mod identities_for_non_unique_public_key_hash; mod identity; mod identity_by_public_key_hash; mod identity_contract_nonce; diff --git a/packages/rs-drive-abci/src/query/service.rs b/packages/rs-drive-abci/src/query/service.rs index 54e51348c0..2ee7a65776 100644 --- a/packages/rs-drive-abci/src/query/service.rs +++ b/packages/rs-drive-abci/src/query/service.rs @@ -22,7 +22,8 @@ use dapi_grpc::platform::v0::{ GetEvonodesProposedEpochBlocksByIdsRequest, GetEvonodesProposedEpochBlocksByRangeRequest, GetEvonodesProposedEpochBlocksResponse, GetIdentitiesBalancesRequest, GetIdentitiesBalancesResponse, GetIdentitiesContractKeysRequest, - GetIdentitiesContractKeysResponse, GetIdentityBalanceAndRevisionRequest, + GetIdentitiesContractKeysResponse, GetIdentitiesForNonUniquePublicKeyHashRequest, + GetIdentitiesForNonUniquePublicKeyHashResponse, GetIdentityBalanceAndRevisionRequest, GetIdentityBalanceAndRevisionResponse, GetIdentityBalanceRequest, GetIdentityBalanceResponse, GetIdentityByPublicKeyHashRequest, GetIdentityByPublicKeyHashResponse, GetIdentityContractNonceRequest, GetIdentityContractNonceResponse, GetIdentityKeysRequest, @@ -404,6 +405,18 @@ impl PlatformService for QueryService { .await } + async fn get_identities_for_non_unique_public_key_hash( + &self, + request: Request, + ) -> Result, Status> { + self.handle_blocking_query( + request, + Platform::::query_identities_for_non_unique_public_key_hash, + "get_identities_for_non_unique_public_key_hash", + ) + .await + } + async fn wait_for_state_transition_result( &self, _request: Request, diff --git a/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_full_identities_for_non_unique_public_key_hash/mod.rs b/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_full_identities_for_non_unique_public_key_hash/mod.rs new file mode 100644 index 0000000000..82c72a8975 --- /dev/null +++ b/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_full_identities_for_non_unique_public_key_hash/mod.rs @@ -0,0 +1,62 @@ +mod v0; + +use crate::drive::Drive; +use crate::error::{drive::DriveError, Error}; +use dpp::identity::Identity; + +use dpp::version::PlatformVersion; +use grovedb::TransactionArg; + +impl Drive { + /// Retrieves all full identities associated with a given non-unique public key hash. + /// + /// This function fetches all identity data associated with a specified public key hash, + /// using the version specified in `PlatformVersion` to determine the correct method implementation. + /// + /// # Parameters + /// + /// - `public_key_hash`: A 20-byte array representing the non-unique public key hash to look up. + /// - `limit`: An optional limit on the number of identities to retrieve. + /// - `transaction`: A `TransactionArg` representing the transaction context. + /// - `platform_version`: A reference to the `PlatformVersion`, which selects the method version for identity fetching. + /// + /// # Returns + /// + /// Returns a `Result` containing a `Vec` where: + /// - Each `Identity` represents a verified identity associated with the public key hash. + /// + /// # Errors + /// + /// Returns an `Error` if: + /// - The provided public key hash does not correspond to any identities. + /// - The method version specified in `PlatformVersion` is unsupported. + /// + pub fn fetch_full_identities_for_non_unique_public_key_hash( + &self, + public_key_hash: [u8; 20], + limit: Option, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + match platform_version + .drive + .methods + .identity + .fetch + .public_key_hashes + .fetch_full_identities_for_non_unique_public_key_hash + { + 0 => self.fetch_full_identities_for_non_unique_public_key_hash_v0( + public_key_hash, + limit, + transaction, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "fetch_full_identities_for_non_unique_public_key_hash".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_full_identities_for_non_unique_public_key_hash/v0/mod.rs b/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_full_identities_for_non_unique_public_key_hash/v0/mod.rs new file mode 100644 index 0000000000..118b2dc04e --- /dev/null +++ b/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_full_identities_for_non_unique_public_key_hash/v0/mod.rs @@ -0,0 +1,55 @@ +use crate::drive::Drive; + +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; + +use dpp::identity::Identity; + +use dpp::version::PlatformVersion; + +use grovedb::TransactionArg; + +impl Drive { + /// Fetches identities with all its information from storage. + pub(super) fn fetch_full_identities_for_non_unique_public_key_hash_v0( + &self, + public_key_hash: [u8; 20], + limit: Option, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + let mut drive_operations: Vec = vec![]; + self.fetch_full_identities_for_non_unique_public_key_hash_operations_v0( + public_key_hash, + limit, + transaction, + &mut drive_operations, + platform_version, + ) + } + + /// Given an identity, fetches the identity with its flags from storage. + pub(super) fn fetch_full_identities_for_non_unique_public_key_hash_operations_v0( + &self, + public_key_hash: [u8; 20], + limit: Option, + transaction: TransactionArg, + drive_operations: &mut Vec, + platform_version: &PlatformVersion, + ) -> Result, Error> { + let identity_ids = self.fetch_identity_ids_by_non_unique_public_key_hash_operations_v0( + public_key_hash, + limit, + transaction, + drive_operations, + &platform_version.drive, + )?; + identity_ids + .into_iter() + .filter_map(|identity_id| { + self.fetch_full_identity(identity_id, transaction, platform_version) + .transpose() + }) + .collect::, Error>>() + } +} diff --git a/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_identity_ids_by_non_unique_public_key_hash/mod.rs b/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_identity_ids_by_non_unique_public_key_hash/mod.rs index 9c444e5973..a5ad808085 100644 --- a/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_identity_ids_by_non_unique_public_key_hash/mod.rs +++ b/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_identity_ids_by_non_unique_public_key_hash/mod.rs @@ -22,6 +22,7 @@ impl Drive { pub fn fetch_identity_ids_by_non_unique_public_key_hash( &self, public_key_hash: [u8; 20], + limit: Option, transaction: TransactionArg, drive_version: &DriveVersion, ) -> Result, Error> { @@ -34,6 +35,7 @@ impl Drive { { 0 => self.fetch_identity_ids_by_non_unique_public_key_hash_v0( public_key_hash, + limit, transaction, drive_version, ), diff --git a/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_identity_ids_by_non_unique_public_key_hash/v0/mod.rs b/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_identity_ids_by_non_unique_public_key_hash/v0/mod.rs index ee462d6328..479f05b69d 100644 --- a/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_identity_ids_by_non_unique_public_key_hash/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/fetch_identity_ids_by_non_unique_public_key_hash/v0/mod.rs @@ -17,12 +17,14 @@ impl Drive { pub(super) fn fetch_identity_ids_by_non_unique_public_key_hash_v0( &self, public_key_hash: [u8; 20], + limit: Option, transaction: TransactionArg, drive_version: &DriveVersion, ) -> Result, Error> { let mut drive_operations: Vec = vec![]; self.fetch_identity_ids_by_non_unique_public_key_hash_operations_v0( public_key_hash, + limit, transaction, &mut drive_operations, drive_version, @@ -30,18 +32,20 @@ impl Drive { } /// Gets identity ids from non unique public key hashes. - pub(super) fn fetch_identity_ids_by_non_unique_public_key_hash_operations_v0( + pub(crate) fn fetch_identity_ids_by_non_unique_public_key_hash_operations_v0( &self, public_key_hash: [u8; 20], + limit: Option, transaction: TransactionArg, drive_operations: &mut Vec, drive_version: &DriveVersion, ) -> Result, Error> { let non_unique_key_hashes = non_unique_key_hashes_sub_tree_path_vec(public_key_hash); - let path_query = PathQuery::new_single_query_item( + let mut path_query = PathQuery::new_single_query_item( non_unique_key_hashes, QueryItem::RangeFull(RangeFull), ); + path_query.query.limit = limit; let (results, _) = self.grove_get_path_query( &path_query, transaction, diff --git a/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/mod.rs b/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/mod.rs index 9a928d7b27..f0eb387965 100644 --- a/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/mod.rs +++ b/packages/rs-drive/src/drive/identity/fetch/fetch_by_public_key_hashes/mod.rs @@ -1,4 +1,5 @@ mod fetch_full_identities_by_unique_public_key_hashes; +mod fetch_full_identities_for_non_unique_public_key_hash; mod fetch_full_identity_by_unique_public_key_hash; mod fetch_identity_id_by_unique_public_key_hash; mod fetch_identity_ids_by_non_unique_public_key_hash; @@ -71,6 +72,7 @@ mod tests { let identity_ids = drive .fetch_identity_ids_by_non_unique_public_key_hash( hash, + None, Some(&transaction), &drive_version, ) diff --git a/packages/rs-drive/src/drive/identity/fetch/prove/mod.rs b/packages/rs-drive/src/drive/identity/fetch/prove/mod.rs index b095b6bf42..40cedc5059 100644 --- a/packages/rs-drive/src/drive/identity/fetch/prove/mod.rs +++ b/packages/rs-drive/src/drive/identity/fetch/prove/mod.rs @@ -1,5 +1,6 @@ mod prove_full_identities; mod prove_full_identities_by_unique_public_key_hashes; +mod prove_full_identities_for_non_unique_public_key_hash; mod prove_full_identity; mod prove_full_identity_by_unique_public_key_hash; mod prove_identities_contract_keys; diff --git a/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identities_by_unique_public_key_hashes/v0/mod.rs b/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identities_by_unique_public_key_hashes/v0/mod.rs index 44f7965678..42ded35eda 100644 --- a/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identities_by_unique_public_key_hashes/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identities_by_unique_public_key_hashes/v0/mod.rs @@ -22,7 +22,7 @@ impl Drive { .into_iter() .map(|(public_key_hash, maybe_identity_id)| { if let Some(identity_id) = maybe_identity_id { - Self::full_identity_with_public_key_hash_query( + Self::full_identity_with_unique_public_key_hash_query( public_key_hash, identity_id, &platform_version.drive.grove_version, diff --git a/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identities_for_non_unique_public_key_hash/mod.rs b/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identities_for_non_unique_public_key_hash/mod.rs new file mode 100644 index 0000000000..23b588cf6c --- /dev/null +++ b/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identities_for_non_unique_public_key_hash/mod.rs @@ -0,0 +1,52 @@ +mod v0; + +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; + +use dpp::version::PlatformVersion; +use grovedb::TransactionArg; + +impl Drive { + /// Fetches the complete identities for a given non-unique public key hash, returning proofs of the identities. + /// + /// This function selects the appropriate handler based on the provided `PlatformVersion` using versioning. + /// + /// # Arguments + /// + /// * `public_key_hash` - A 20-byte array representing the public key hash for which identities are requested. + /// * `transaction` - The transaction context for the operation. + /// * `platform_version` - A reference to the platform version, determining the method version to call. + /// + /// # Returns + /// + /// Returns a `Result` containing a vector of bytes representing the proved identities. If the operation fails or the + /// version is unsupported, an `Error` is returned. + pub fn prove_full_identities_for_non_unique_public_key_hash( + &self, + public_key_hash: [u8; 20], + limit: Option, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + match platform_version + .drive + .methods + .identity + .prove + .prove_full_identities_for_non_unique_public_key_hash + { + 0 => self.prove_full_identities_for_non_unique_public_key_hash_v0( + public_key_hash, + limit, + transaction, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "prove_full_identities_for_non_unique_public_key_hash".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identities_for_non_unique_public_key_hash/v0/mod.rs b/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identities_for_non_unique_public_key_hash/v0/mod.rs new file mode 100644 index 0000000000..70e577e0e9 --- /dev/null +++ b/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identities_for_non_unique_public_key_hash/v0/mod.rs @@ -0,0 +1,215 @@ +use crate::drive::{non_unique_key_hashes_sub_tree_path_vec, Drive}; + +use crate::error::Error; + +use crate::query::QueryItem; +use dpp::version::PlatformVersion; +use grovedb::{PathQuery, TransactionArg}; + +impl Drive { + /// Given public key hash, fetches up to 5 full identities as proofs. + pub(super) fn prove_full_identities_for_non_unique_public_key_hash_v0( + &self, + public_key_hash: [u8; 20], + limit: Option, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result, Error> { + let identity_ids = self.fetch_identity_ids_by_non_unique_public_key_hash( + public_key_hash, + limit, + transaction, + &platform_version.drive, + )?; + + let non_unique_key_hashes = non_unique_key_hashes_sub_tree_path_vec(public_key_hash); + let identity_ids_query = if let Some(last) = identity_ids.last() { + PathQuery::new_single_query_item( + non_unique_key_hashes, + QueryItem::RangeToInclusive(..=last.to_vec()), + ) + } else { + Self::identity_ids_for_non_unique_public_key_hash_query(public_key_hash) + }; + + let mut path_queries = identity_ids + .into_iter() + .map(|identity_id| { + Self::full_identity_query(&identity_id, &platform_version.drive.grove_version) + }) + .collect::, Error>>()?; + + path_queries.push(identity_ids_query); + + let path_query = PathQuery::merge( + path_queries.iter().collect(), + &platform_version.drive.grove_version, + ) + .map_err(Error::GroveDB)?; + self.grove_get_proved_path_query( + &path_query, + transaction, + &mut vec![], + &platform_version.drive, + ) + } +} + +#[cfg(test)] +mod tests { + use crate::drive::Drive; + use crate::util::test_helpers::setup::setup_drive_with_initial_state_structure; + use bincode::error::DecodeError; + use dpp::block::block_info::BlockInfo; + use dpp::identity::accessors::IdentityGettersV0; + use dpp::identity::identity_public_key::methods::hash::IdentityPublicKeyHashMethodsV0; + use dpp::identity::{Identity, KeyType, Purpose, SecurityLevel}; + use dpp::prelude::IdentityPublicKey; + use dpp::version::PlatformVersion; + use grovedb::operations::proof::GroveDBProof; + use rand::rngs::StdRng; + use rand::SeedableRng; + use std::collections::BTreeMap; + + #[test] + fn should_prove_multiple_identities() { + let drive = setup_drive_with_initial_state_structure(None); + + let platform_version = PlatformVersion::latest(); + + let mut identities: BTreeMap<[u8; 32], Identity> = + Identity::random_identities(2, 3, Some(14), platform_version) + .expect("expected random identities") + .into_iter() + .map(|identity| (identity.id().to_buffer(), identity)) + .collect(); + + let mut rng = StdRng::seed_from_u64(394); + + let (public_key, _) = IdentityPublicKey::random_key_with_known_attributes( + 4, + &mut rng, + Purpose::AUTHENTICATION, + SecurityLevel::MEDIUM, + KeyType::ECDSA_HASH160, + None, + platform_version, + ) + .expect("expected key"); + + for identity in identities.values_mut() { + identity.add_public_key(public_key.clone()); + drive + .add_new_identity( + identity.clone(), + false, + &BlockInfo::default(), + true, + None, + platform_version, + ) + .expect("expected to add an identity"); + } + + let public_key_hash = public_key + .public_key_hash() + .expect("expected public key hash"); + + let proof = drive + .prove_full_identities_for_non_unique_public_key_hash( + public_key_hash, + Some(3), + None, + platform_version, + ) + .expect("should not error when proving an identity"); + + let (_, proved_identities): ([u8; 32], Vec) = + Drive::verify_full_identities_for_non_unique_public_key_hash( + proof.as_slice(), + public_key_hash, + Some(3), + platform_version, + ) + .expect("expect that this be verified"); + + assert_eq!(proved_identities.len(), 2); + } + + #[test] + fn should_prove_multiple_identities_limit_under_total() { + let drive = setup_drive_with_initial_state_structure(None); + + let platform_version = PlatformVersion::latest(); + + let mut identities: BTreeMap<[u8; 32], Identity> = + Identity::random_identities(10, 3, Some(14), platform_version) + .expect("expected random identities") + .into_iter() + .map(|identity| (identity.id().to_buffer(), identity)) + .collect(); + + let mut rng = StdRng::seed_from_u64(394); + + let (public_key, _) = IdentityPublicKey::random_key_with_known_attributes( + 4, + &mut rng, + Purpose::AUTHENTICATION, + SecurityLevel::MEDIUM, + KeyType::ECDSA_HASH160, + None, + platform_version, + ) + .expect("expected key"); + + for identity in identities.values_mut() { + identity.add_public_key(public_key.clone()); + drive + .add_new_identity( + identity.clone(), + false, + &BlockInfo::default(), + true, + None, + platform_version, + ) + .expect("expected to add an identity"); + } + + let public_key_hash = public_key + .public_key_hash() + .expect("expected public key hash"); + + let proof = drive + .prove_full_identities_for_non_unique_public_key_hash( + public_key_hash, + Some(3), + None, + platform_version, + ) + .expect("should not error when proving an identity"); + + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + let grovedb_proof: Result = + bincode::decode_from_slice(&proof, config).map(|(a, _)| a); + + let grovedb_proof_string = match grovedb_proof { + Ok(proof) => format!("{}", proof), + Err(_) => "Invalid GroveDBProof".to_string(), + }; + println!("{}", grovedb_proof_string); + + let (_, proved_identities): ([u8; 32], Vec) = + Drive::verify_full_identities_for_non_unique_public_key_hash( + proof.as_slice(), + public_key_hash, + Some(3), + platform_version, + ) + .expect("expect that this be verified"); + + assert_eq!(proved_identities.len(), 3); + } +} diff --git a/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identity_by_unique_public_key_hash/v0/mod.rs b/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identity_by_unique_public_key_hash/v0/mod.rs index f474fa5231..c967106830 100644 --- a/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identity_by_unique_public_key_hash/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identity_by_unique_public_key_hash/v0/mod.rs @@ -20,7 +20,7 @@ impl Drive { platform_version, )?; if let Some(identity_id) = identity_id { - let query = Self::full_identity_with_public_key_hash_query( + let query = Self::full_identity_with_unique_public_key_hash_query( public_key_hash, identity_id, &platform_version.drive.grove_version, diff --git a/packages/rs-drive/src/drive/identity/fetch/prove/prove_identity_id_by_unique_public_key_hash/v0/mod.rs b/packages/rs-drive/src/drive/identity/fetch/prove/prove_identity_id_by_unique_public_key_hash/v0/mod.rs index 8900c2e2dc..83af15538f 100644 --- a/packages/rs-drive/src/drive/identity/fetch/prove/prove_identity_id_by_unique_public_key_hash/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/fetch/prove/prove_identity_id_by_unique_public_key_hash/v0/mod.rs @@ -65,7 +65,7 @@ mod tests { ) .expect("should not error when proving an identity"); - let (_, proved_identity_id) = Drive::verify_identity_id_by_public_key_hash( + let (_, proved_identity_id) = Drive::verify_identity_id_by_unique_public_key_hash( proof.as_slice(), false, first_key_hash, diff --git a/packages/rs-drive/src/drive/identity/fetch/prove/prove_identity_ids_by_unique_public_key_hashes/v0/mod.rs b/packages/rs-drive/src/drive/identity/fetch/prove/prove_identity_ids_by_unique_public_key_hashes/v0/mod.rs index 1b4636e6b5..d9c54243db 100644 --- a/packages/rs-drive/src/drive/identity/fetch/prove/prove_identity_ids_by_unique_public_key_hashes/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/fetch/prove/prove_identity_ids_by_unique_public_key_hashes/v0/mod.rs @@ -89,7 +89,7 @@ mod tests { .expect("should not error when proving an identity"); let (_, proved_identity_id): ([u8; 32], BTreeMap<[u8; 20], Option<[u8; 32]>>) = - Drive::verify_identity_ids_by_public_key_hashes( + Drive::verify_identity_ids_by_unique_public_key_hashes( proof.as_slice(), false, &key_hashes, diff --git a/packages/rs-drive/src/drive/identity/fetch/queries/mod.rs b/packages/rs-drive/src/drive/identity/fetch/queries/mod.rs index f7832d2274..b52cc01c13 100644 --- a/packages/rs-drive/src/drive/identity/fetch/queries/mod.rs +++ b/packages/rs-drive/src/drive/identity/fetch/queries/mod.rs @@ -1,6 +1,9 @@ use crate::drive::balances::balance_path_vec; use crate::drive::identity::key::fetch::IdentityKeysRequest; -use crate::drive::{identity_tree_path_vec, unique_key_hashes_tree_path_vec, Drive}; +use crate::drive::{ + identity_tree_path_vec, non_unique_key_hashes_sub_tree_path_vec, + unique_key_hashes_tree_path_vec, Drive, +}; use std::ops::RangeFull; use crate::error::Error; @@ -90,6 +93,14 @@ impl Drive { PathQuery::new_single_key(unique_key_hashes, public_key_hash.to_vec()) } + /// The query for proving identity ids from a public key hash for non-unique keys + pub fn identity_ids_for_non_unique_public_key_hash_query( + public_key_hash: [u8; 20], + ) -> PathQuery { + let non_unique_key_hashes = non_unique_key_hashes_sub_tree_path_vec(public_key_hash); + PathQuery::new_single_query_item(non_unique_key_hashes, QueryItem::RangeFull(RangeFull)) + } + /// The query for proving identity ids from a vector of public key hashes. pub fn identity_ids_by_unique_public_key_hash_query( public_key_hashes: &[[u8; 20]], @@ -198,7 +209,7 @@ impl Drive { } /// This query gets the full identity and the public key hash - pub fn full_identity_with_public_key_hash_query( + pub fn full_identity_with_unique_public_key_hash_query( public_key_hash: [u8; 20], identity_id: [u8; 32], grove_version: &GroveVersion, diff --git a/packages/rs-drive/src/query/mod.rs b/packages/rs-drive/src/query/mod.rs index 7491454a64..8ca767ff46 100644 --- a/packages/rs-drive/src/query/mod.rs +++ b/packages/rs-drive/src/query/mod.rs @@ -2014,9 +2014,7 @@ mod tests { use serde_json::Value::Null; use crate::config::DriveConfig; - use crate::util::test_helpers::setup::{ - setup_drive_with_initial_state_structure, setup_system_data_contract, - }; + use crate::util::test_helpers::setup::setup_drive_with_initial_state_structure; use dpp::block::block_info::BlockInfo; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contracts::SystemDataContract; diff --git a/packages/rs-drive/src/verify/identity/mod.rs b/packages/rs-drive/src/verify/identity/mod.rs index dd24b00f67..57e28d4047 100644 --- a/packages/rs-drive/src/verify/identity/mod.rs +++ b/packages/rs-drive/src/verify/identity/mod.rs @@ -1,4 +1,5 @@ mod verify_full_identities_by_public_key_hashes; +mod verify_full_identities_for_non_unique_public_key_hash; mod verify_full_identity_by_identity_id; mod verify_full_identity_by_public_key_hash; mod verify_identities_contract_keys; @@ -6,8 +7,9 @@ mod verify_identity_balance_and_revision_for_identity_id; mod verify_identity_balance_for_identity_id; mod verify_identity_balances_for_identity_ids; mod verify_identity_contract_nonce; -mod verify_identity_id_by_public_key_hash; -mod verify_identity_ids_by_public_key_hashes; +mod verify_identity_id_by_unique_public_key_hash; +mod verify_identity_ids_by_unique_public_key_hashes; +mod verify_identity_ids_for_non_unique_public_key_hash; mod verify_identity_keys_by_identity_id; mod verify_identity_nonce; mod verify_identity_revision_for_identity_id; diff --git a/packages/rs-drive/src/verify/identity/verify_full_identities_by_public_key_hashes/v0/mod.rs b/packages/rs-drive/src/verify/identity/verify_full_identities_by_public_key_hashes/v0/mod.rs index 7c1f0545d0..43ce89e6d0 100644 --- a/packages/rs-drive/src/verify/identity/verify_full_identities_by_public_key_hashes/v0/mod.rs +++ b/packages/rs-drive/src/verify/identity/verify_full_identities_by_public_key_hashes/v0/mod.rs @@ -48,7 +48,7 @@ impl Drive { platform_version: &PlatformVersion, ) -> Result<(RootHash, T), Error> { let (root_hash, identity_ids_by_key_hashes) = - Self::verify_identity_ids_by_public_key_hashes::>( + Self::verify_identity_ids_by_unique_public_key_hashes::>( proof, true, public_key_hashes, diff --git a/packages/rs-drive/src/verify/identity/verify_full_identities_for_non_unique_public_key_hash/mod.rs b/packages/rs-drive/src/verify/identity/verify_full_identities_for_non_unique_public_key_hash/mod.rs new file mode 100644 index 0000000000..9a4d5537b1 --- /dev/null +++ b/packages/rs-drive/src/verify/identity/verify_full_identities_for_non_unique_public_key_hash/mod.rs @@ -0,0 +1,74 @@ +mod v0; + +use crate::drive::Drive; + +use crate::error::drive::DriveError; + +use crate::error::Error; + +use crate::verify::RootHash; + +pub use dpp::prelude::Identity; + +use dpp::version::PlatformVersion; + +use std::iter::FromIterator; + +impl Drive { + /// Verifies and retrieves the full identities associated with a given non-unique public key hash. + /// + /// This function validates the provided proof to confirm identity IDs for a specified public key hash, + /// and then retrieves the full identity data for each verified identity ID. It uses the versioning + /// specified in `PlatformVersion` to select the correct method implementation. + /// + /// # Type Parameters + /// + /// - `T`: The output collection type for identities, which must implement `FromIterator`. + /// + /// # Parameters + /// + /// - `proof`: A byte slice containing the proof data to verify the identities. + /// - `public_key_hash`: A 20-byte array representing the non-unique public key hash. + /// - `limit`: An optional limit for the number of identities to retrieve. + /// - `platform_version`: A reference to the `PlatformVersion`, used to determine the verification method version. + /// + /// # Returns + /// + /// Returns a `Result` containing: + /// - `RootHash`: The root hash from GroveDB after proof verification. + /// - `T`: A collection of verified `Identity` instances corresponding to the public key hash. + /// + /// # Errors + /// + /// Returns an `Error` if: + /// - The provided proof is invalid or incomplete. + /// - No full identity data is available for any of the retrieved identity IDs. + /// - The method version specified in `PlatformVersion` is not supported. + /// + pub fn verify_full_identities_for_non_unique_public_key_hash>( + proof: &[u8], + public_key_hash: [u8; 20], + limit: Option, + platform_version: &PlatformVersion, + ) -> Result<(RootHash, T), Error> { + match platform_version + .drive + .methods + .verify + .identity + .verify_full_identities_for_non_unique_public_key_hash + { + 0 => Self::verify_full_identities_for_non_unique_public_key_hash_v0( + proof, + public_key_hash, + limit, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "verify_full_identities_for_non_unique_public_key_hash".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/verify/identity/verify_full_identities_for_non_unique_public_key_hash/v0/mod.rs b/packages/rs-drive/src/verify/identity/verify_full_identities_for_non_unique_public_key_hash/v0/mod.rs new file mode 100644 index 0000000000..168692c0ee --- /dev/null +++ b/packages/rs-drive/src/verify/identity/verify_full_identities_for_non_unique_public_key_hash/v0/mod.rs @@ -0,0 +1,74 @@ +use crate::drive::Drive; + +use crate::error::proof::ProofError; +use crate::error::Error; + +use crate::verify::RootHash; + +pub use dpp::prelude::Identity; + +use dpp::version::PlatformVersion; + +impl Drive { + /// Verifies and retrieves full identities associated with a given public key hash. + /// + /// This function validates the provided proof to confirm the identity IDs corresponding to a non-unique public key hash + /// and subsequently verifies the full identity data for each of those identity IDs. + /// + /// # Type Parameters + /// + /// - `T`: The output collection type, which must implement `FromIterator`. + /// + /// # Parameters + /// + /// - `proof`: A byte slice containing the proof for verifying the identities. + /// - `public_key_hash`: A 20-byte array representing the hash of the user's public key. + /// - `limit`: An optional limit for the number of identities to retrieve. + /// - `platform_version`: A reference to the `PlatformVersion`, which dictates the GroveDB version used for verification. + /// + /// # Returns + /// + /// Returns a `Result` containing: + /// - `RootHash`: The root hash from GroveDB after proof verification. + /// - `T`: A collection of verified `Identity` instances, created from the retrieved identity IDs. + /// + /// # Errors + /// + /// Returns an `Error` if: + /// - The provided proof is invalid or incomplete. + /// - No full identity data is available for any of the retrieved identity IDs. + /// - The proof includes incorrect paths or keys. + /// + pub(super) fn verify_full_identities_for_non_unique_public_key_hash_v0< + T: FromIterator, + >( + proof: &[u8], + public_key_hash: [u8; 20], + limit: Option, + platform_version: &PlatformVersion, + ) -> Result<(RootHash, T), Error> { + let (root_hash, identity_ids) = Self::verify_identity_ids_for_non_unique_public_key_hash( + proof, + true, + public_key_hash, + limit, + platform_version, + )?; + let identities = identity_ids + .into_iter() + .map(|identity_id| { + let identity = Self::verify_full_identity_by_identity_id( + proof, + true, + identity_id, + platform_version, + ) + .map(|(_, maybe_identity)| maybe_identity)?; + identity.ok_or(Error::Proof(ProofError::IncompleteProof( + "proof returned an identity id without identity information", + ))) + }) + .collect::>()?; + Ok((root_hash, identities)) + } +} diff --git a/packages/rs-drive/src/verify/identity/verify_full_identity_by_public_key_hash/v0/mod.rs b/packages/rs-drive/src/verify/identity/verify_full_identity_by_public_key_hash/v0/mod.rs index e481343470..abc9882c76 100644 --- a/packages/rs-drive/src/verify/identity/verify_full_identity_by_public_key_hash/v0/mod.rs +++ b/packages/rs-drive/src/verify/identity/verify_full_identity_by_public_key_hash/v0/mod.rs @@ -43,7 +43,7 @@ impl Drive { public_key_hash: [u8; 20], platform_version: &PlatformVersion, ) -> Result<(RootHash, Option), Error> { - let (root_hash, identity_id) = Self::verify_identity_id_by_public_key_hash( + let (root_hash, identity_id) = Self::verify_identity_id_by_unique_public_key_hash( proof, true, public_key_hash, diff --git a/packages/rs-drive/src/verify/identity/verify_identity_id_by_public_key_hash/mod.rs b/packages/rs-drive/src/verify/identity/verify_identity_id_by_unique_public_key_hash/mod.rs similarity index 86% rename from packages/rs-drive/src/verify/identity/verify_identity_id_by_public_key_hash/mod.rs rename to packages/rs-drive/src/verify/identity/verify_identity_id_by_unique_public_key_hash/mod.rs index 75182ffeef..4422e600e0 100644 --- a/packages/rs-drive/src/verify/identity/verify_identity_id_by_public_key_hash/mod.rs +++ b/packages/rs-drive/src/verify/identity/verify_identity_id_by_unique_public_key_hash/mod.rs @@ -33,7 +33,7 @@ impl Drive { /// - An unknown or unsupported platform version is provided. /// - Any other error as documented in the specific versioned function. /// - pub fn verify_identity_id_by_public_key_hash( + pub fn verify_identity_id_by_unique_public_key_hash( proof: &[u8], is_proof_subset: bool, public_key_hash: [u8; 20], @@ -44,16 +44,16 @@ impl Drive { .methods .verify .identity - .verify_identity_id_by_public_key_hash + .verify_identity_id_by_unique_public_key_hash { - 0 => Self::verify_identity_id_by_public_key_hash_v0( + 0 => Self::verify_identity_id_by_unique_public_key_hash_v0( proof, is_proof_subset, public_key_hash, platform_version, ), version => Err(Error::Drive(DriveError::UnknownVersionMismatch { - method: "verify_identity_id_by_public_key_hash".to_string(), + method: "verify_identity_id_by_unique_public_key_hash".to_string(), known_versions: vec![0], received: version, })), diff --git a/packages/rs-drive/src/verify/identity/verify_identity_id_by_public_key_hash/v0/mod.rs b/packages/rs-drive/src/verify/identity/verify_identity_id_by_unique_public_key_hash/v0/mod.rs similarity index 98% rename from packages/rs-drive/src/verify/identity/verify_identity_id_by_public_key_hash/v0/mod.rs rename to packages/rs-drive/src/verify/identity/verify_identity_id_by_unique_public_key_hash/v0/mod.rs index ceb8ae05b0..cccc1aa0f1 100644 --- a/packages/rs-drive/src/verify/identity/verify_identity_id_by_public_key_hash/v0/mod.rs +++ b/packages/rs-drive/src/verify/identity/verify_identity_id_by_unique_public_key_hash/v0/mod.rs @@ -33,7 +33,7 @@ impl Drive { /// - More than one identity ID is found. /// #[inline(always)] - pub(super) fn verify_identity_id_by_public_key_hash_v0( + pub(super) fn verify_identity_id_by_unique_public_key_hash_v0( proof: &[u8], is_proof_subset: bool, public_key_hash: [u8; 20], diff --git a/packages/rs-drive/src/verify/identity/verify_identity_ids_by_public_key_hashes/mod.rs b/packages/rs-drive/src/verify/identity/verify_identity_ids_by_unique_public_key_hashes/mod.rs similarity index 87% rename from packages/rs-drive/src/verify/identity/verify_identity_ids_by_public_key_hashes/mod.rs rename to packages/rs-drive/src/verify/identity/verify_identity_ids_by_unique_public_key_hashes/mod.rs index 20a9d98c3d..6d1f70f8a0 100644 --- a/packages/rs-drive/src/verify/identity/verify_identity_ids_by_public_key_hashes/mod.rs +++ b/packages/rs-drive/src/verify/identity/verify_identity_ids_by_unique_public_key_hashes/mod.rs @@ -36,7 +36,7 @@ impl Drive { /// - An unknown or unsupported platform version is provided. /// - Any other error as documented in the specific versioned function. /// - pub fn verify_identity_ids_by_public_key_hashes< + pub fn verify_identity_ids_by_unique_public_key_hashes< T: FromIterator<([u8; 20], Option<[u8; 32]>)>, >( proof: &[u8], @@ -49,16 +49,16 @@ impl Drive { .methods .verify .identity - .verify_identity_ids_by_public_key_hashes + .verify_identity_ids_by_unique_public_key_hashes { - 0 => Self::verify_identity_ids_by_public_key_hashes_v0( + 0 => Self::verify_identity_ids_by_unique_public_key_hashes_v0( proof, is_proof_subset, public_key_hashes, platform_version, ), version => Err(Error::Drive(DriveError::UnknownVersionMismatch { - method: "verify_identity_ids_by_public_key_hashes".to_string(), + method: "verify_identity_ids_by_unique_public_key_hashes".to_string(), known_versions: vec![0], received: version, })), diff --git a/packages/rs-drive/src/verify/identity/verify_identity_ids_by_public_key_hashes/v0/mod.rs b/packages/rs-drive/src/verify/identity/verify_identity_ids_by_unique_public_key_hashes/v0/mod.rs similarity index 98% rename from packages/rs-drive/src/verify/identity/verify_identity_ids_by_public_key_hashes/v0/mod.rs rename to packages/rs-drive/src/verify/identity/verify_identity_ids_by_unique_public_key_hashes/v0/mod.rs index 6d1c6dc7d8..90beab8eaf 100644 --- a/packages/rs-drive/src/verify/identity/verify_identity_ids_by_public_key_hashes/v0/mod.rs +++ b/packages/rs-drive/src/verify/identity/verify_identity_ids_by_unique_public_key_hashes/v0/mod.rs @@ -37,7 +37,7 @@ impl Drive { /// - The number of proved key values does not match the number of public key hashes provided. /// - The value size of the identity ID is incorrect. /// - pub(crate) fn verify_identity_ids_by_public_key_hashes_v0< + pub(crate) fn verify_identity_ids_by_unique_public_key_hashes_v0< T: FromIterator<([u8; 20], Option<[u8; 32]>)>, >( proof: &[u8], diff --git a/packages/rs-drive/src/verify/identity/verify_identity_ids_for_non_unique_public_key_hash/mod.rs b/packages/rs-drive/src/verify/identity/verify_identity_ids_for_non_unique_public_key_hash/mod.rs new file mode 100644 index 0000000000..88f61f30a3 --- /dev/null +++ b/packages/rs-drive/src/verify/identity/verify_identity_ids_for_non_unique_public_key_hash/mod.rs @@ -0,0 +1,68 @@ +mod v0; + +use crate::drive::Drive; + +use crate::error::drive::DriveError; + +use crate::error::Error; + +use crate::verify::RootHash; + +use dpp::version::PlatformVersion; + +impl Drive { + /// Verifies and retrieves identity IDs associated with a non-unique public key hash. + /// + /// This function checks the provided proof and confirms the validity of identity IDs for a given public key hash. + /// It utilizes versioning to call the appropriate method based on the `PlatformVersion`. + /// + /// # Parameters + /// + /// - `proof`: A byte slice representing the proof to verify the identity data. + /// - `is_proof_subset`: A boolean indicating if the proof represents a subset query. + /// - `public_key_hash`: A 20-byte array representing the hash of the user’s public key. + /// - `limit`: An optional limit on the number of identity IDs to retrieve. + /// - `platform_version`: A reference to the `PlatformVersion` struct, which selects the correct verification method version. + /// + /// # Returns + /// + /// Returns a `Result` containing: + /// - `RootHash`: The root hash of GroveDB after proof verification. + /// - `Vec<[u8; 32]>`: A vector of identity IDs (each as a 32-byte array) associated with the public key hash. + /// + /// # Errors + /// + /// Returns an `Error` if: + /// - The provided proof is invalid or fails verification. + /// - The public key hash does not correspond to any valid identity IDs. + /// - The method version specified in `PlatformVersion` is not supported. + /// + pub fn verify_identity_ids_for_non_unique_public_key_hash( + proof: &[u8], + is_proof_subset: bool, + public_key_hash: [u8; 20], + limit: Option, + platform_version: &PlatformVersion, + ) -> Result<(RootHash, Vec<[u8; 32]>), Error> { + match platform_version + .drive + .methods + .verify + .identity + .verify_identity_ids_for_non_unique_public_key_hash + { + 0 => Self::verify_identity_ids_for_non_unique_public_key_hash_v0( + proof, + is_proof_subset, + public_key_hash, + limit, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "verify_identity_ids_for_non_unique_public_key_hash".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/verify/identity/verify_identity_ids_for_non_unique_public_key_hash/v0/mod.rs b/packages/rs-drive/src/verify/identity/verify_identity_ids_for_non_unique_public_key_hash/v0/mod.rs new file mode 100644 index 0000000000..5330737908 --- /dev/null +++ b/packages/rs-drive/src/verify/identity/verify_identity_ids_for_non_unique_public_key_hash/v0/mod.rs @@ -0,0 +1,83 @@ +use crate::drive::{non_unique_key_hashes_sub_tree_path, Drive}; + +use crate::error::proof::ProofError; +use crate::error::Error; + +use crate::verify::RootHash; + +use grovedb::GroveDb; +use platform_version::version::PlatformVersion; + +impl Drive { + /// Verifies and retrieves identity IDs for a user based on their public key hash. + /// + /// This function verifies the provided proof to confirm the identity ID(s) associated with a non-unique public key hash. + /// It ensures the proof is valid and matches the expected structure and path within GroveDB. + /// + /// # Parameters + /// + /// - `proof`: A byte slice representing the authentication proof to verify the identity. + /// - `is_proof_subset`: A boolean indicating whether the proof represents a subset query. + /// - `public_key_hash`: A 20-byte array representing the hash of the user's public key. + /// - `limit`: An optional limit for the number of identity IDs to retrieve. + /// - `platform_version`: A reference to the `PlatformVersion` which determines the GroveDB version. + /// + /// # Returns + /// + /// Returns a `Result` containing: + /// - `RootHash`: The GroveDB root hash for the verified proof. + /// - `Vec<[u8; 32]>`: A vector of identity IDs (each as a 32-byte array) corresponding to the public key hash, if any are found. + /// + /// # Errors + /// + /// Returns an `Error` if: + /// + /// - The authentication proof is invalid or corrupted. + /// - The public key hash does not map to any valid identity ID. + /// - The proof includes incorrect paths or keys in the non-unique key hash tree. + /// - Multiple identity IDs are found when only one was expected. + /// + #[inline(always)] + pub(super) fn verify_identity_ids_for_non_unique_public_key_hash_v0( + proof: &[u8], + is_proof_subset: bool, + public_key_hash: [u8; 20], + limit: Option, + platform_version: &PlatformVersion, + ) -> Result<(RootHash, Vec<[u8; 32]>), Error> { + let mut path_query = + Self::identity_ids_for_non_unique_public_key_hash_query(public_key_hash); + path_query.query.limit = Some( + limit.unwrap_or( + platform_version + .drive_abci + .query + .max_returned_full_identities, + ), + ); + let (root_hash, proved_key_values) = if is_proof_subset { + GroveDb::verify_subset_query(proof, &path_query, &platform_version.drive.grove_version)? + } else { + GroveDb::verify_query(proof, &path_query, &platform_version.drive.grove_version)? + }; + + let identity_ids = proved_key_values + .into_iter() + .map(|(path, key, _)| { + if path != non_unique_key_hashes_sub_tree_path(&public_key_hash) { + return Err(Error::Proof(ProofError::CorruptedProof( + "we did not get back an element for the correct path in non unique key hashes" + .to_string(), + ))); + } + key.try_into().map_err(|_| { + Error::Proof(ProofError::CorruptedProof( + "key should be 32 bytes in non unique key hash tree".to_string(), + )) + }) + }) + .collect::, Error>>()?; + + Ok((root_hash, identity_ids)) + } +} diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/mod.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/mod.rs index 8b010c9c3d..4491853ec5 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/mod.rs @@ -5,6 +5,7 @@ use versioned_feature_core::{FeatureVersion, FeatureVersionBounds}; #[derive(Clone, Debug, Default)] pub struct DriveAbciQueryVersions { pub max_returned_elements: u16, + pub max_returned_full_identities: u16, pub response_metadata: FeatureVersion, pub proofs_query: FeatureVersionBounds, pub document_query: FeatureVersionBounds, @@ -32,6 +33,7 @@ pub struct DriveAbciQueryIdentityVersions { pub identities_balances: FeatureVersionBounds, pub balance_and_revision: FeatureVersionBounds, pub identity_by_public_key_hash: FeatureVersionBounds, + pub identities_for_non_unique_public_key_hash: FeatureVersionBounds, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/v1.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/v1.rs index b6af08405c..92d699bb84 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/v1.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_query_versions/v1.rs @@ -7,6 +7,7 @@ use versioned_feature_core::FeatureVersionBounds; pub const DRIVE_ABCI_QUERY_VERSIONS_V1: DriveAbciQueryVersions = DriveAbciQueryVersions { max_returned_elements: 100, + max_returned_full_identities: 3, response_metadata: 0, proofs_query: FeatureVersionBounds { min_version: 0, @@ -71,6 +72,11 @@ pub const DRIVE_ABCI_QUERY_VERSIONS_V1: DriveAbciQueryVersions = DriveAbciQueryV max_version: 0, default_current_version: 0, }, + identities_for_non_unique_public_key_hash: FeatureVersionBounds { + min_version: 0, + max_version: 0, + default_current_version: 0, + }, }, validator_queries: DriveAbciQueryValidatorVersions { proposed_block_counts_by_evonode_ids: FeatureVersionBounds { diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/mod.rs b/packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/mod.rs index c44788e4c3..8108790671 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/mod.rs @@ -89,6 +89,7 @@ pub struct DriveIdentityFetchPublicKeyHashesMethodVersions { pub has_non_unique_public_key_hash: FeatureVersion, pub has_non_unique_public_key_hash_already_for_identity: FeatureVersion, pub has_unique_public_key_hash: FeatureVersion, + pub fetch_full_identities_for_non_unique_public_key_hash: FeatureVersion, } #[derive(Clone, Debug, Default)] @@ -127,6 +128,7 @@ pub struct DriveIdentityProveMethodVersions { pub prove_full_identity_by_unique_public_key_hash: FeatureVersion, pub prove_identity_id_by_unique_public_key_hash: FeatureVersion, pub prove_identity_ids_by_unique_public_key_hashes: FeatureVersion, + pub prove_full_identities_for_non_unique_public_key_hash: FeatureVersion, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/v1.rs b/packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/v1.rs index 098f38ab7c..08dba38566 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/v1.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/v1.rs @@ -27,6 +27,7 @@ pub const DRIVE_IDENTITY_METHOD_VERSIONS_V1: DriveIdentityMethodVersions = has_non_unique_public_key_hash: 0, has_non_unique_public_key_hash_already_for_identity: 0, has_unique_public_key_hash: 0, + fetch_full_identities_for_non_unique_public_key_hash: 0, }, attributes: DriveIdentityFetchAttributesMethodVersions { revision: 0, @@ -58,6 +59,7 @@ pub const DRIVE_IDENTITY_METHOD_VERSIONS_V1: DriveIdentityMethodVersions = prove_full_identity_by_unique_public_key_hash: 0, prove_identity_id_by_unique_public_key_hash: 0, prove_identity_ids_by_unique_public_key_hashes: 0, + prove_full_identities_for_non_unique_public_key_hash: 0, }, keys: DriveIdentityKeysMethodVersions { fetch: DriveIdentityKeysFetchMethodVersions { diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/mod.rs b/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/mod.rs index 39d4255136..fcbe1b6412 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/mod.rs @@ -29,17 +29,19 @@ pub struct DriveVerifyDocumentMethodVersions { #[derive(Clone, Debug, Default)] pub struct DriveVerifyIdentityMethodVersions { pub verify_full_identities_by_public_key_hashes: FeatureVersion, + pub verify_full_identities_for_non_unique_public_key_hash: FeatureVersion, pub verify_full_identity_by_identity_id: FeatureVersion, pub verify_full_identity_by_public_key_hash: FeatureVersion, pub verify_identity_balance_for_identity_id: FeatureVersion, pub verify_identity_balances_for_identity_ids: FeatureVersion, - pub verify_identity_id_by_public_key_hash: FeatureVersion, - pub verify_identity_ids_by_public_key_hashes: FeatureVersion, + pub verify_identity_id_by_unique_public_key_hash: FeatureVersion, + pub verify_identity_ids_by_unique_public_key_hashes: FeatureVersion, pub verify_identity_keys_by_identity_id: FeatureVersion, pub verify_identity_nonce: FeatureVersion, pub verify_identity_contract_nonce: FeatureVersion, pub verify_identities_contract_keys: FeatureVersion, pub verify_identity_revision_for_identity_id: FeatureVersion, + pub verify_identity_ids_for_non_unique_public_key_hash: FeatureVersion, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/v1.rs b/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/v1.rs index 4c40371f83..90dab7bb4d 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/v1.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_verify_method_versions/v1.rs @@ -17,17 +17,19 @@ pub const DRIVE_VERIFY_METHOD_VERSIONS_V1: DriveVerifyMethodVersions = DriveVeri }, identity: DriveVerifyIdentityMethodVersions { verify_full_identities_by_public_key_hashes: 0, + verify_full_identities_for_non_unique_public_key_hash: 0, verify_full_identity_by_identity_id: 0, verify_full_identity_by_public_key_hash: 0, verify_identity_balance_for_identity_id: 0, verify_identity_balances_for_identity_ids: 0, - verify_identity_id_by_public_key_hash: 0, - verify_identity_ids_by_public_key_hashes: 0, + verify_identity_id_by_unique_public_key_hash: 0, + verify_identity_ids_by_unique_public_key_hashes: 0, verify_identity_keys_by_identity_id: 0, verify_identity_nonce: 0, verify_identity_contract_nonce: 0, verify_identities_contract_keys: 0, verify_identity_revision_for_identity_id: 0, + verify_identity_ids_for_non_unique_public_key_hash: 0, }, single_document: DriveVerifySingleDocumentMethodVersions { verify_proof: 0, diff --git a/packages/rs-platform-version/src/version/mocks/v2_test.rs b/packages/rs-platform-version/src/version/mocks/v2_test.rs index 931ef19b97..505511d43a 100644 --- a/packages/rs-platform-version/src/version/mocks/v2_test.rs +++ b/packages/rs-platform-version/src/version/mocks/v2_test.rs @@ -139,6 +139,7 @@ pub const TEST_PLATFORM_V2: PlatformVersion = PlatformVersion { withdrawal_constants: DRIVE_ABCI_WITHDRAWAL_CONSTANTS_V1, query: DriveAbciQueryVersions { max_returned_elements: 100, + max_returned_full_identities: 3, response_metadata: 0, proofs_query: FeatureVersionBounds { min_version: 0, @@ -203,6 +204,11 @@ pub const TEST_PLATFORM_V2: PlatformVersion = PlatformVersion { max_version: 0, default_current_version: 0, }, + identities_for_non_unique_public_key_hash: FeatureVersionBounds { + min_version: 0, + max_version: 0, + default_current_version: 0, + }, }, validator_queries: DriveAbciQueryValidatorVersions { proposed_block_counts_by_evonode_ids: FeatureVersionBounds { diff --git a/packages/rs-sdk/src/platform/fetch_many.rs b/packages/rs-sdk/src/platform/fetch_many.rs index 4653835557..05e73bbeed 100644 --- a/packages/rs-sdk/src/platform/fetch_many.rs +++ b/packages/rs-sdk/src/platform/fetch_many.rs @@ -6,6 +6,7 @@ //! - `[FetchMany]`: An async trait that fetches multiple items of a specific type from Platform. use super::LimitQuery; +use crate::platform::types::identity::NonUniquePublicKeyHash; use crate::{ error::Error, mock::MockResponse, @@ -18,13 +19,14 @@ use dapi_grpc::platform::v0::{ GetContestedResourceVotersForIdentityRequest, GetContestedResourcesRequest, GetDataContractsRequest, GetEpochsInfoRequest, GetEvonodesProposedEpochBlocksByIdsRequest, GetEvonodesProposedEpochBlocksByRangeRequest, GetIdentitiesBalancesRequest, - GetIdentityKeysRequest, GetPathElementsRequest, GetProtocolVersionUpgradeStateRequest, - GetProtocolVersionUpgradeVoteStatusRequest, GetVotePollsByEndDateRequest, + GetIdentitiesForNonUniquePublicKeyHashRequest, GetIdentityKeysRequest, GetPathElementsRequest, + GetProtocolVersionUpgradeStateRequest, GetProtocolVersionUpgradeVoteStatusRequest, + GetVotePollsByEndDateRequest, }; use dashcore_rpc::dashcore::ProTxHash; use dpp::data_contract::DataContract; use dpp::identity::KeyID; -use dpp::prelude::{Identifier, IdentityPublicKey}; +use dpp::prelude::{Identifier, Identity, IdentityPublicKey}; use dpp::util::deserializer::ProtocolVersion; use dpp::version::ProtocolVersionVoteCount; use dpp::{block::epoch::EpochIndex, prelude::TimestampMillis, voting::vote_polls::VotePoll}; @@ -458,6 +460,15 @@ impl FetchMany for drive_proof_verifier::types::Id type Request = GetIdentitiesBalancesRequest; } +/// Fetch multiple elements. +/// +/// ## Supported query types +/// +/// * [NonUniquePublicKeyHash] +impl FetchMany for NonUniquePublicKeyHash { + type Request = GetIdentitiesForNonUniquePublicKeyHashRequest; +} + // /// Fetch multiple elements. /// diff --git a/packages/rs-sdk/src/platform/types/identity.rs b/packages/rs-sdk/src/platform/types/identity.rs index 4b7b7754d0..45b78437c9 100644 --- a/packages/rs-sdk/src/platform/types/identity.rs +++ b/packages/rs-sdk/src/platform/types/identity.rs @@ -7,6 +7,7 @@ use crate::{ }; use dapi_grpc::platform::v0::get_identities_balances_request::GetIdentitiesBalancesRequestV0; +use dapi_grpc::platform::v0::get_identities_for_non_unique_public_key_hash_request::GetIdentitiesForNonUniquePublicKeyHashRequestV0; use dapi_grpc::platform::v0::get_identity_balance_and_revision_request::GetIdentityBalanceAndRevisionRequestV0; use dapi_grpc::platform::v0::get_identity_balance_request::GetIdentityBalanceRequestV0; use dapi_grpc::platform::v0::get_identity_by_public_key_hash_request::GetIdentityByPublicKeyHashRequestV0; @@ -14,12 +15,13 @@ use dapi_grpc::platform::v0::get_identity_contract_nonce_request::GetIdentityCon use dapi_grpc::platform::v0::get_identity_nonce_request::GetIdentityNonceRequestV0; use dapi_grpc::platform::v0::get_identity_request::GetIdentityRequestV0; use dapi_grpc::platform::v0::{ - get_identities_balances_request, get_identity_balance_and_revision_request, - get_identity_balance_request, get_identity_by_public_key_hash_request, - get_identity_contract_nonce_request, get_identity_nonce_request, get_identity_request, - GetIdentitiesBalancesRequest, GetIdentityBalanceAndRevisionRequest, GetIdentityBalanceRequest, - GetIdentityByPublicKeyHashRequest, GetIdentityContractNonceRequest, GetIdentityNonceRequest, - GetIdentityRequest, ResponseMetadata, + get_identities_balances_request, get_identities_for_non_unique_public_key_hash_request, + get_identity_balance_and_revision_request, get_identity_balance_request, + get_identity_by_public_key_hash_request, get_identity_contract_nonce_request, + get_identity_nonce_request, get_identity_request, GetIdentitiesBalancesRequest, + GetIdentitiesForNonUniquePublicKeyHashRequest, GetIdentityBalanceAndRevisionRequest, + GetIdentityBalanceRequest, GetIdentityByPublicKeyHashRequest, GetIdentityContractNonceRequest, + GetIdentityNonceRequest, GetIdentityRequest, ResponseMetadata, }; use dpp::prelude::Identity; use rs_dapi_client::transport::TransportError; @@ -56,6 +58,13 @@ impl Query for dpp::prelude::Identifier { #[derive(Clone, Debug, PartialEq, Eq)] pub struct PublicKeyHash(pub [u8; 20]); +/// Non-unique Public key hash that can be used as a [Query] to find identities. +/// +/// You can use [`FetchMany::fetch_many(Vec)`](crate::platform::FetchMany::fetch_many()) to fetch the identities +/// having this public key hash. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct NonUniquePublicKeyHash(pub [u8; 20]); + impl Query for PublicKeyHash { fn query(self, prove: bool) -> Result { if !prove { @@ -165,3 +174,25 @@ impl Query for Vec { Ok(request) } } + +impl Query for NonUniquePublicKeyHash { + fn query(self, prove: bool) -> Result { + if !prove { + unimplemented!("queries without proofs are not supported yet"); + } + + let request: GetIdentitiesForNonUniquePublicKeyHashRequest = + GetIdentitiesForNonUniquePublicKeyHashRequest { + version: Some( + get_identities_for_non_unique_public_key_hash_request::Version::V0( + GetIdentitiesForNonUniquePublicKeyHashRequestV0 { + public_key_hash: self.0.to_vec(), + prove, + }, + ), + ), + }; + + Ok(request) + } +}