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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/rs-sdk/src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub use {
fetch_many::FetchMany,
fetch_unproved::FetchUnproved,
query::{
LimitQuery, ProposerBlockCountByIdsQuery, Query, QueryStartInfo, DEFAULT_EPOCH_QUERY_LIMIT,
IdentityKeysQuery, LimitQuery, ProposerBlockCountByIdsQuery, Query, QueryStartInfo,
DEFAULT_EPOCH_QUERY_LIMIT,
},
};
82 changes: 81 additions & 1 deletion packages/rs-sdk/src/platform/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ use dapi_grpc::platform::v0::{
GetCurrentQuorumsInfoRequest, GetEpochsInfoRequest, GetEvonodesProposedEpochBlocksByIdsRequest,
GetEvonodesProposedEpochBlocksByRangeRequest, GetIdentityKeysRequest, GetPathElementsRequest,
GetProtocolVersionUpgradeStateRequest, GetProtocolVersionUpgradeVoteStatusRequest,
GetTotalCreditsInPlatformRequest, KeyRequestType,
GetTotalCreditsInPlatformRequest, KeyRequestType, SpecificKeys,
};
use dapi_grpc::platform::v0::{
get_status_request, GetContestedResourceIdentityVotesRequest,
GetPrefundedSpecializedBalanceRequest, GetStatusRequest, GetTokenDirectPurchasePricesRequest,
GetTokenPerpetualDistributionLastClaimRequest, GetVotePollsByEndDateRequest,
};
use dpp::dashcore_rpc::dashcore::{hashes::Hash, ProTxHash};
use dpp::identity::KeyID;
use dpp::version::PlatformVersionError;
use dpp::{block::epoch::EpochIndex, prelude::Identifier};
use drive::query::contested_resource_votes_given_by_identity_query::ContestedResourceVotesGivenByIdentityQuery;
Expand Down Expand Up @@ -183,6 +184,85 @@ impl Query<proto::GetIdentityKeysRequest> for Identifier {
}
}

/// Query for specific identity keys by their IDs
#[derive(Debug, Clone)]
pub struct IdentityKeysQuery {
/// Identity ID to fetch keys from
pub identity_id: Identifier,
/// Specific key IDs to fetch
pub key_ids: Vec<KeyID>,
/// Optional limit for the number of keys to return
pub limit: Option<u32>,
/// Optional offset for pagination
pub offset: Option<u32>,
}

impl IdentityKeysQuery {
/// Create a new query for specific identity keys
///
/// # Arguments
///
/// * `identity_id` - The identity to fetch keys from
/// * `key_ids` - The specific key IDs to fetch
///
/// # Example
///
/// ```rust
/// use dash_sdk::platform::{Identifier, IdentityKeysQuery};
///
/// let identity_id = Identifier::new([1; 32]);
/// let key_ids = vec![0, 1, 2]; // Fetch keys with IDs 0, 1, and 2
/// let query = IdentityKeysQuery::new(identity_id, key_ids);
/// ```
pub fn new(identity_id: Identifier, key_ids: Vec<KeyID>) -> Self {
Self {
identity_id,
key_ids,
limit: None,
offset: None,
}
}

/// Set a limit on the number of keys to return
pub fn with_limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}

/// Set an offset for pagination
pub fn with_offset(mut self, offset: u32) -> Self {
self.offset = Some(offset);
self
}
}

impl Query<proto::GetIdentityKeysRequest> for IdentityKeysQuery {
/// Get specific keys for an identity.
fn query(self, prove: bool) -> Result<proto::GetIdentityKeysRequest, Error> {
if !prove {
unimplemented!("queries without proofs are not supported yet");
}

Ok(GetIdentityKeysRequest {
version: Some(get_identity_keys_request::Version::V0(
GetIdentityKeysRequestV0 {
identity_id: self.identity_id.to_vec(),
prove,
limit: self.limit,
offset: self.offset,
request_type: Some(KeyRequestType {
request: Some(proto::key_request_type::Request::SpecificKeys(
SpecificKeys {
key_ids: self.key_ids.into_iter().collect(),
},
)),
}),
},
)),
})
}
}

impl Query<DocumentQuery> for DriveDocumentQuery<'_> {
fn query(self, prove: bool) -> Result<DocumentQuery, Error> {
if !prove {
Expand Down
71 changes: 70 additions & 1 deletion packages/rs-sdk/tests/fetch/identity.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use dash_sdk::platform::types::identity::{NonUniquePublicKeyHashQuery, PublicKeyHash};
use dash_sdk::platform::{Fetch, FetchMany};
use dash_sdk::platform::{Fetch, FetchMany, IdentityKeysQuery};
use dpp::identity::accessors::IdentityGettersV0;
use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0;
use dpp::prelude::IdentityPublicKey;
use dpp::{identity::hash::IdentityPublicKeyHashMethodsV0, prelude::Identity};
use drive_proof_verifier::types::{IdentityBalance, IdentityBalanceAndRevision};
use std::collections::BTreeSet;

use super::{common::setup_logs, config::Config};

Expand Down Expand Up @@ -117,6 +118,74 @@ async fn test_identity_public_keys_all_read() {
}
}

/// Given some existing identity ID and selected key IDs, when I fetch specific identity keys, I get only those keys.
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_identity_public_keys_specific_read() {
setup_logs();

let cfg = Config::new();
let identity_id: dpp::prelude::Identifier = cfg.existing_identity_id;

let sdk = cfg
.setup_api("test_identity_public_keys_specific_read")
.await;

let all_public_keys = IdentityPublicKey::fetch_many(&sdk, identity_id)
.await
.expect("fetch identity public keys");

assert!(
!all_public_keys.is_empty(),
"identity must expose at least one public key"
);

let requested_key_ids = vec![0, 2];

let query = IdentityKeysQuery::new(identity_id, requested_key_ids.clone());

let fetched_subset = IdentityPublicKey::fetch_many(&sdk, query)
.await
.expect("fetch selected identity public keys");

assert_eq!(
fetched_subset.len(),
requested_key_ids.len(),
"number of fetched keys should match the requested set"
);

let requested_key_set: BTreeSet<u32> = requested_key_ids.iter().copied().collect();

for key_id in &requested_key_ids {
let expected = all_public_keys
.get(key_id)
.and_then(|value| value.as_ref())
.expect("expected key in base dataset");

let actual = fetched_subset
.get(key_id)
.and_then(|value| value.as_ref())
.expect("expected key in fetched subset");

assert_eq!(
actual, expected,
"fetched key {} does not match the original key",
key_id
);
}

let unexpected: Vec<u32> = fetched_subset
.keys()
.filter(|key| !requested_key_set.contains(key))
.copied()
.collect();

assert!(
unexpected.is_empty(),
"subset should not include unrequested keys: {:?}",
unexpected
);
}

/// Given some non-unique public key, when I fetch identity that uses this key, I get associated identities containing this key.
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_fetch_identity_by_non_unique_public_keys() {
Expand Down
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8ea8f1a8f5a17e04776eaa4f5341466503e8cbf2edc6e3298d6441ea69e0c9b4ec1cccc2d2d81f261fd2260589654a53
Loading