Skip to content

Commit 37bdbf4

Browse files
authored
feat(rs-sdk): identity keys query (#2806)
1 parent aa3e1cf commit 37bdbf4

7 files changed

+154
-3
lines changed

packages/rs-sdk/src/platform.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub use {
3838
fetch_many::FetchMany,
3939
fetch_unproved::FetchUnproved,
4040
query::{
41-
LimitQuery, ProposerBlockCountByIdsQuery, Query, QueryStartInfo, DEFAULT_EPOCH_QUERY_LIMIT,
41+
IdentityKeysQuery, LimitQuery, ProposerBlockCountByIdsQuery, Query, QueryStartInfo,
42+
DEFAULT_EPOCH_QUERY_LIMIT,
4243
},
4344
};

packages/rs-sdk/src/platform/query.rs

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ use dapi_grpc::platform::v0::{
2323
GetCurrentQuorumsInfoRequest, GetEpochsInfoRequest, GetEvonodesProposedEpochBlocksByIdsRequest,
2424
GetEvonodesProposedEpochBlocksByRangeRequest, GetIdentityKeysRequest, GetPathElementsRequest,
2525
GetProtocolVersionUpgradeStateRequest, GetProtocolVersionUpgradeVoteStatusRequest,
26-
GetTotalCreditsInPlatformRequest, KeyRequestType,
26+
GetTotalCreditsInPlatformRequest, KeyRequestType, SpecificKeys,
2727
};
2828
use dapi_grpc::platform::v0::{
2929
get_status_request, GetContestedResourceIdentityVotesRequest,
3030
GetPrefundedSpecializedBalanceRequest, GetStatusRequest, GetTokenDirectPurchasePricesRequest,
3131
GetTokenPerpetualDistributionLastClaimRequest, GetVotePollsByEndDateRequest,
3232
};
3333
use dpp::dashcore_rpc::dashcore::{hashes::Hash, ProTxHash};
34+
use dpp::identity::KeyID;
3435
use dpp::version::PlatformVersionError;
3536
use dpp::{block::epoch::EpochIndex, prelude::Identifier};
3637
use drive::query::contested_resource_votes_given_by_identity_query::ContestedResourceVotesGivenByIdentityQuery;
@@ -183,6 +184,85 @@ impl Query<proto::GetIdentityKeysRequest> for Identifier {
183184
}
184185
}
185186

187+
/// Query for specific identity keys by their IDs
188+
#[derive(Debug, Clone)]
189+
pub struct IdentityKeysQuery {
190+
/// Identity ID to fetch keys from
191+
pub identity_id: Identifier,
192+
/// Specific key IDs to fetch
193+
pub key_ids: Vec<KeyID>,
194+
/// Optional limit for the number of keys to return
195+
pub limit: Option<u32>,
196+
/// Optional offset for pagination
197+
pub offset: Option<u32>,
198+
}
199+
200+
impl IdentityKeysQuery {
201+
/// Create a new query for specific identity keys
202+
///
203+
/// # Arguments
204+
///
205+
/// * `identity_id` - The identity to fetch keys from
206+
/// * `key_ids` - The specific key IDs to fetch
207+
///
208+
/// # Example
209+
///
210+
/// ```rust
211+
/// use dash_sdk::platform::{Identifier, IdentityKeysQuery};
212+
///
213+
/// let identity_id = Identifier::new([1; 32]);
214+
/// let key_ids = vec![0, 1, 2]; // Fetch keys with IDs 0, 1, and 2
215+
/// let query = IdentityKeysQuery::new(identity_id, key_ids);
216+
/// ```
217+
pub fn new(identity_id: Identifier, key_ids: Vec<KeyID>) -> Self {
218+
Self {
219+
identity_id,
220+
key_ids,
221+
limit: None,
222+
offset: None,
223+
}
224+
}
225+
226+
/// Set a limit on the number of keys to return
227+
pub fn with_limit(mut self, limit: u32) -> Self {
228+
self.limit = Some(limit);
229+
self
230+
}
231+
232+
/// Set an offset for pagination
233+
pub fn with_offset(mut self, offset: u32) -> Self {
234+
self.offset = Some(offset);
235+
self
236+
}
237+
}
238+
239+
impl Query<proto::GetIdentityKeysRequest> for IdentityKeysQuery {
240+
/// Get specific keys for an identity.
241+
fn query(self, prove: bool) -> Result<proto::GetIdentityKeysRequest, Error> {
242+
if !prove {
243+
unimplemented!("queries without proofs are not supported yet");
244+
}
245+
246+
Ok(GetIdentityKeysRequest {
247+
version: Some(get_identity_keys_request::Version::V0(
248+
GetIdentityKeysRequestV0 {
249+
identity_id: self.identity_id.to_vec(),
250+
prove,
251+
limit: self.limit,
252+
offset: self.offset,
253+
request_type: Some(KeyRequestType {
254+
request: Some(proto::key_request_type::Request::SpecificKeys(
255+
SpecificKeys {
256+
key_ids: self.key_ids.into_iter().collect(),
257+
},
258+
)),
259+
}),
260+
},
261+
)),
262+
})
263+
}
264+
}
265+
186266
impl Query<DocumentQuery> for DriveDocumentQuery<'_> {
187267
fn query(self, prove: bool) -> Result<DocumentQuery, Error> {
188268
if !prove {

packages/rs-sdk/tests/fetch/identity.rs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use dash_sdk::platform::types::identity::{NonUniquePublicKeyHashQuery, PublicKeyHash};
2-
use dash_sdk::platform::{Fetch, FetchMany};
2+
use dash_sdk::platform::{Fetch, FetchMany, IdentityKeysQuery};
33
use dpp::identity::accessors::IdentityGettersV0;
44
use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0;
55
use dpp::prelude::IdentityPublicKey;
66
use dpp::{identity::hash::IdentityPublicKeyHashMethodsV0, prelude::Identity};
77
use drive_proof_verifier::types::{IdentityBalance, IdentityBalanceAndRevision};
8+
use std::collections::BTreeSet;
89

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

@@ -117,6 +118,74 @@ async fn test_identity_public_keys_all_read() {
117118
}
118119
}
119120

121+
/// Given some existing identity ID and selected key IDs, when I fetch specific identity keys, I get only those keys.
122+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
123+
async fn test_identity_public_keys_specific_read() {
124+
setup_logs();
125+
126+
let cfg = Config::new();
127+
let identity_id: dpp::prelude::Identifier = cfg.existing_identity_id;
128+
129+
let sdk = cfg
130+
.setup_api("test_identity_public_keys_specific_read")
131+
.await;
132+
133+
let all_public_keys = IdentityPublicKey::fetch_many(&sdk, identity_id)
134+
.await
135+
.expect("fetch identity public keys");
136+
137+
assert!(
138+
!all_public_keys.is_empty(),
139+
"identity must expose at least one public key"
140+
);
141+
142+
let requested_key_ids = vec![0, 2];
143+
144+
let query = IdentityKeysQuery::new(identity_id, requested_key_ids.clone());
145+
146+
let fetched_subset = IdentityPublicKey::fetch_many(&sdk, query)
147+
.await
148+
.expect("fetch selected identity public keys");
149+
150+
assert_eq!(
151+
fetched_subset.len(),
152+
requested_key_ids.len(),
153+
"number of fetched keys should match the requested set"
154+
);
155+
156+
let requested_key_set: BTreeSet<u32> = requested_key_ids.iter().copied().collect();
157+
158+
for key_id in &requested_key_ids {
159+
let expected = all_public_keys
160+
.get(key_id)
161+
.and_then(|value| value.as_ref())
162+
.expect("expected key in base dataset");
163+
164+
let actual = fetched_subset
165+
.get(key_id)
166+
.and_then(|value| value.as_ref())
167+
.expect("expected key in fetched subset");
168+
169+
assert_eq!(
170+
actual, expected,
171+
"fetched key {} does not match the original key",
172+
key_id
173+
);
174+
}
175+
176+
let unexpected: Vec<u32> = fetched_subset
177+
.keys()
178+
.filter(|key| !requested_key_set.contains(key))
179+
.copied()
180+
.collect();
181+
182+
assert!(
183+
unexpected.is_empty(),
184+
"subset should not include unrequested keys: {:?}",
185+
unexpected
186+
);
187+
}
188+
120189
/// Given some non-unique public key, when I fetch identity that uses this key, I get associated identities containing this key.
121190
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
122191
async fn test_fetch_identity_by_non_unique_public_keys() {

packages/rs-sdk/tests/vectors/test_identity_public_keys_specific_read/.gitkeep

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
8ea8f1a8f5a17e04776eaa4f5341466503e8cbf2edc6e3298d6441ea69e0c9b4ec1cccc2d2d81f261fd2260589654a53

0 commit comments

Comments
 (0)