Skip to content

Commit ca2a8fe

Browse files
committed
feat(sdk): get identity by non-unique pubkey hashes
chore: update to latest dash core 37 (#2483) feat(platform)!: token advanced distribution and updates (#2471) fix: token history contract (#2474) Co-authored-by: Ivan Shumkov <ivan@shumkov.ru> Co-authored-by: QuantumExplorer <quantum@dash.org> fix(drive): using new rust dash core methods for reversed quorum hash to maintain backwards compatibility (#2489) feat: more granular integer document property types (#2455) Co-authored-by: Quantum Explorer <quantum@dash.org> docs: update comment for data contract code range (#2476) feat: validate token name localizations (#2468) feat(sdk): get identity by non-unique keys build(deps): update grovedb to current develop test: test identity by non-unique pubkey hashes fix(sdk): dash core client fails to get quorum chore: minor fixes test(drive-abci): identity by non-unique pubkey start after chore: minor changes to verify feat(sdk): token and group queries (#2449) chore: revert limit 1 => limit none chore: add non-unique key to test identities test(sdk): test vectors for test_fetch_identity_by_non_unique_public_keys fix(platform)!: token distribution fixes and tests (#2494) chore(platform): bump to version 2.0.0-dev.1 (#2495) test: update assertion fix(sdk): make some things public (#2496) feat(platform): require token for document actions (#2498) fix: data contract proof doesn't work with new auto fields (#2501)
1 parent 2dd0619 commit ca2a8fe

File tree

22 files changed

+276
-30
lines changed

22 files changed

+276
-30
lines changed

packages/dapi-grpc/build.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ fn configure_platform(mut platform: MappingConfig) -> MappingConfig {
6363
// Derive features for versioned messages
6464
//
6565
// "GetConsensusParamsRequest" is excluded as this message does not support proofs
66-
const VERSIONED_REQUESTS: [&str; 40] = [
66+
const VERSIONED_REQUESTS: [&str; 41] = [
6767
"GetDataContractHistoryRequest",
6868
"GetDataContractRequest",
6969
"GetDataContractsRequest",
@@ -75,6 +75,7 @@ fn configure_platform(mut platform: MappingConfig) -> MappingConfig {
7575
"GetIdentityContractNonceRequest",
7676
"GetIdentityBalanceAndRevisionRequest",
7777
"GetIdentityBalanceRequest",
78+
"GetIdentityByNonUniquePublicKeyHashRequest",
7879
"GetIdentityByPublicKeyHashRequest",
7980
"GetIdentityKeysRequest",
8081
"GetIdentityRequest",
@@ -110,6 +111,9 @@ fn configure_platform(mut platform: MappingConfig) -> MappingConfig {
110111
// - "GetConsensusParamsResponse"
111112
// - "GetStatusResponse"
112113
//
114+
// The following responses are excluded as they need custom proof handling:
115+
// - "GetIdentityByNonUniquePublicKeyHashResponse"
116+
//
113117
// "GetEvonodesProposedEpochBlocksResponse" is used for 2 Requests
114118
const VERSIONED_RESPONSES: [&str; 39] = [
115119
"GetDataContractHistoryResponse",

packages/rs-dapi-client/src/transport/grpc.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,15 @@ impl_transport_request_grpc!(
490490
get_status
491491
);
492492

493+
// rpc getIdentityByNonUniquePublicKeyHash(GetIdentityByNonUniquePublicKeyHashRequest) returns (GetIdentityByNonUniquePublicKeyHashResponse);
494+
impl_transport_request_grpc!(
495+
platform_proto::GetIdentityByNonUniquePublicKeyHashRequest,
496+
platform_proto::GetIdentityByNonUniquePublicKeyHashResponse,
497+
PlatformGrpcClient,
498+
RequestSettings::default(),
499+
get_identity_by_non_unique_public_key_hash
500+
);
501+
493502
// rpc getIdentityTokenBalances(GetIdentityTokenBalancesRequest) returns (GetIdentityTokenBalancesResponse);
494503
impl_transport_request_grpc!(
495504
platform_proto::GetIdentityTokenBalancesRequest,

packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,20 @@ impl<C> Platform<C> {
110110
transaction: TransactionArg,
111111
platform_version: &PlatformVersion,
112112
) -> Result<(), Error> {
113+
let mut rng = StdRng::seed_from_u64(0u64);
114+
let non_unique_key =
115+
IdentityPublicKey::random_voting_key_with_rng(11, &mut rng, platform_version)?;
116+
113117
for id in [IDENTITY_ID_1, IDENTITY_ID_2, IDENTITY_ID_3] {
114118
// Create identity without keys
115119
let mut identity = Identity::create_basic_identity(id, platform_version)?;
116120

117121
// Generate keys
118122
let seed = id.to_buffer()[0];
119123
let mut rng = StdRng::seed_from_u64(seed as u64);
120-
let keys = IdentityPublicKey::main_keys_with_random_authentication_keys_with_private_keys_with_rng(3, &mut rng, platform_version)?;
124+
let mut keys = IdentityPublicKey::main_keys_with_random_authentication_keys_with_private_keys_with_rng(3, &mut rng, platform_version)?;
125+
// every identity has the same non-unique key
126+
keys.push(non_unique_key.clone());
121127

122128
for (key, private_key) in keys.iter() {
123129
let private_key = hex::encode(private_key);

packages/rs-drive-abci/src/query/identity_based_queries/identity_by_non_unique_public_key_hash/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use dapi_grpc::platform::v0::get_identity_by_non_unique_public_key_hash_request:
77
use dapi_grpc::platform::v0::get_identity_by_non_unique_public_key_hash_response::Version as ResponseVersion;
88
use dapi_grpc::platform::v0::{
99
GetIdentityByNonUniquePublicKeyHashRequest, GetIdentityByNonUniquePublicKeyHashResponse,
10-
GetIdentityByPublicKeyHashResponse,
1110
};
1211
use dpp::version::PlatformVersion;
1312

@@ -28,7 +27,8 @@ impl<C> Platform<C> {
2827
),
2928
));
3029
};
31-
30+
// TODO why `identity_by_unique_public_key_hash`?
31+
// Shouldn't we rename or add new field like `identity_by_non_unique_public_key_hash`?
3232
let feature_version_bounds = &platform_version
3333
.drive_abci
3434
.query

packages/rs-drive-abci/src/query/identity_based_queries/identity_by_non_unique_public_key_hash/v0/mod.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl<C> Platform<C> {
3838
)
3939
.map(|bytes| bytes.0)
4040
.map_err(|_| QueryError::InvalidArgument(
41-
"public key hash must be 20 bytes long".to_string()
41+
"start_after must be 32 bytes long identity ID".to_string()
4242
))))
4343
} else {
4444
None
@@ -129,6 +129,33 @@ mod tests {
129129
));
130130
}
131131

132+
#[test]
133+
fn test_invalid_start_after() {
134+
let (platform, state, version) = setup_platform(None, Network::Testnet, None);
135+
136+
let negative_tests: Vec<&[u8]> = vec![&[0u8; 4], &[0u8; 20], &[0u8; 64]];
137+
138+
for test in negative_tests {
139+
let request = GetIdentityByNonUniquePublicKeyHashRequestV0 {
140+
public_key_hash: vec![0; 20],
141+
start_after: Some(test.to_vec()),
142+
prove: false,
143+
};
144+
145+
let result = platform
146+
.query_identity_by_non_unique_public_key_hash_v0(request, &state, version)
147+
.expect("expected query to succeed");
148+
149+
assert!(
150+
matches!(
151+
result.errors.as_slice(),
152+
[QueryError::InvalidArgument(msg)] if msg == &"start_after must be 32 bytes long identity ID".to_string()),
153+
"errors: {:?}",
154+
result.errors,
155+
);
156+
}
157+
}
158+
132159
#[test]
133160
fn test_identity_not_found() {
134161
let (platform, state, version) = setup_platform(None, Network::Testnet, None);

packages/rs-drive-proof-verifier/src/proof.rs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ use dapi_grpc::platform::v0::get_protocol_version_upgrade_vote_status_request::{
1515
self, GetProtocolVersionUpgradeVoteStatusRequestV0,
1616
};
1717
use dapi_grpc::platform::v0::security_level_map::KeyKindRequestType as GrpcKeyKind;
18-
use dapi_grpc::platform::v0::{get_contested_resource_identity_votes_request, get_data_contract_history_request, get_data_contract_request, get_data_contracts_request, get_epochs_info_request, get_evonodes_proposed_epoch_blocks_by_ids_request, get_evonodes_proposed_epoch_blocks_by_range_request, get_group_actions_request, get_group_info_request, get_group_infos_request, get_identities_balances_request, get_identities_contract_keys_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_keys_request, get_identity_nonce_request, get_identity_request, get_path_elements_request, get_prefunded_specialized_balance_request, GetContestedResourceVotersForIdentityRequest, GetContestedResourceVotersForIdentityResponse, GetGroupActionSignersRequest, GetGroupActionSignersResponse, GetGroupActionsRequest, GetGroupActionsResponse, GetGroupInfoRequest, GetGroupInfoResponse, GetGroupInfosRequest, GetGroupInfosResponse, GetPathElementsRequest, GetPathElementsResponse, GetProtocolVersionUpgradeStateRequest, GetProtocolVersionUpgradeStateResponse, GetProtocolVersionUpgradeVoteStatusRequest, GetProtocolVersionUpgradeVoteStatusResponse, Proof, ResponseMetadata};
18+
use dapi_grpc::platform::v0::{
19+
get_contested_resource_identity_votes_request, get_data_contract_history_request, get_data_contract_request, get_data_contracts_request, get_epochs_info_request, get_evonodes_proposed_epoch_blocks_by_ids_request, get_evonodes_proposed_epoch_blocks_by_range_request, get_identities_balances_request, get_identities_contract_keys_request, get_identity_balance_and_revision_request, get_identity_balance_request, get_identity_by_non_unique_public_key_hash_request,
20+
get_identity_by_public_key_hash_request, get_identity_contract_nonce_request, get_identity_keys_request, get_identity_nonce_request, get_identity_request, get_path_elements_request, get_prefunded_specialized_balance_request, GetContestedResourceVotersForIdentityRequest, GetContestedResourceVotersForIdentityResponse, GetPathElementsRequest, GetPathElementsResponse, GetProtocolVersionUpgradeStateRequest, GetProtocolVersionUpgradeStateResponse, GetProtocolVersionUpgradeVoteStatusRequest, GetProtocolVersionUpgradeVoteStatusResponse, Proof, ResponseMetadata
21+
};
1922
use dapi_grpc::platform::{
2023
v0::{self as platform, key_request_type, KeyRequestType as GrpcKeyType},
2124
VersionedGrpcResponse,
@@ -36,6 +39,7 @@ use dpp::state_transition::proof_result::StateTransitionProofResult;
3639
use dpp::state_transition::StateTransition;
3740
use dpp::version::PlatformVersion;
3841
use dpp::voting::votes::Vote;
42+
use drive::drive::identity::identity_and_non_unique_public_key_hash_double_proof::IdentityAndNonUniquePublicKeyHashDoubleProof;
3943
use drive::drive::identity::key::fetch::{
4044
IdentityKeysRequest, KeyKindRequestType, KeyRequestType, PurposeU8, SecurityLevelU8,
4145
};
@@ -339,6 +343,98 @@ impl FromProof<platform::GetIdentityByPublicKeyHashRequest> for Identity {
339343
}
340344
}
341345

346+
impl FromProof<platform::GetIdentityByNonUniquePublicKeyHashRequest> for Identity {
347+
type Request = platform::GetIdentityByNonUniquePublicKeyHashRequest;
348+
type Response = platform::GetIdentityByNonUniquePublicKeyHashResponse;
349+
fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
350+
request: I,
351+
response: O,
352+
_network: Network,
353+
platform_version: &PlatformVersion,
354+
provider: &'a dyn ContextProvider,
355+
) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
356+
where
357+
Self: Sized + 'a,
358+
{
359+
let request = request.into();
360+
let response = response.into();
361+
// Parse response to read proof and metadata
362+
// note that proof in this case is different
363+
// let proof = response.proof().or(Err(Error::NoProofInResult))?;
364+
use platform::get_identity_by_non_unique_public_key_hash_response::{
365+
get_identity_by_non_unique_public_key_hash_response_v0::Result as V0Result, Version::V0,
366+
};
367+
368+
let (proved_response, mtd) = match response.version {
369+
Some(V0(v0)) => {
370+
let proof = if let V0Result::Proof(p) = v0.result.ok_or(Error::NoProofInResult)? {
371+
p
372+
} else {
373+
return Err(Error::NoProofInResult);
374+
};
375+
376+
(proof, v0.metadata.ok_or(Error::EmptyResponseMetadata)?)
377+
}
378+
_ => return Err(Error::EmptyResponseMetadata),
379+
};
380+
381+
// let mtd = response.metadata().or(Err(Error::EmptyResponseMetadata))?;
382+
383+
let (public_key_hash, after_identity) = match request.version.ok_or(Error::EmptyVersion)? {
384+
get_identity_by_non_unique_public_key_hash_request::Version::V0(v0) => {
385+
let public_key_hash =
386+
v0.public_key_hash
387+
.try_into()
388+
.map_err(|_| Error::RequestError {
389+
error: "Invalid public key hash length".to_string(),
390+
})?;
391+
392+
let after = v0
393+
.start_after
394+
.map(|a| {
395+
a.try_into().map_err(|_| Error::RequestError {
396+
error: "Invalid start_after length".to_string(),
397+
})
398+
})
399+
.transpose()?;
400+
(public_key_hash, after)
401+
}
402+
};
403+
404+
// we need to convert some data to handle non-default proof structure for this response
405+
let proof = proved_response
406+
.grovedb_identity_public_key_hash_proof
407+
.ok_or(Error::NoProofInResult)?;
408+
409+
let proof_tuple = IdentityAndNonUniquePublicKeyHashDoubleProof {
410+
identity_proof: proved_response.identity_proof_bytes,
411+
identity_id_public_key_hash_proof: proof.grovedb_proof.clone(),
412+
};
413+
414+
// Extract content from proof and verify Drive/GroveDB proofs
415+
let (root_hash, maybe_identity) =
416+
Drive::verify_full_identity_by_non_unique_public_key_hash(
417+
&proof_tuple,
418+
public_key_hash,
419+
after_identity,
420+
platform_version,
421+
)
422+
.map_err(|e| match e {
423+
drive::error::Error::GroveDB(e) => Error::GroveDBError {
424+
proof_bytes: proof.grovedb_proof.clone(),
425+
height: mtd.height,
426+
time_ms: mtd.time_ms,
427+
error: e.to_string(),
428+
},
429+
_ => e.into(),
430+
})?;
431+
432+
verify_tenderdash_proof(&proof, &mtd, &root_hash, provider)?;
433+
434+
Ok((maybe_identity, mtd.clone(), proof))
435+
}
436+
}
437+
342438
impl FromProof<platform::GetIdentityKeysRequest> for IdentityPublicKeys {
343439
type Request = platform::GetIdentityKeysRequest;
344440
type Response = platform::GetIdentityKeysResponse;

packages/rs-drive-verify-c-binding/src/lib.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub unsafe extern "C" fn verify_full_identity_by_identity_id(
100100
}
101101

102102
#[no_mangle]
103-
pub unsafe extern "C" fn verify_identity_id_by_public_key_hash(
103+
pub unsafe extern "C" fn verify_identity_id_by_unique_public_key_hash(
104104
proof_array: *const u8,
105105
proof_len: usize,
106106
is_proof_subset: bool,
@@ -109,8 +109,11 @@ pub unsafe extern "C" fn verify_identity_id_by_public_key_hash(
109109
let proof = unsafe { slice::from_raw_parts(proof_array, proof_len) };
110110
let public_key_hash = unsafe { std::ptr::read(public_key_hash) };
111111

112-
let verification_result =
113-
Drive::verify_identity_id_by_public_key_hash(proof, is_proof_subset, public_key_hash);
112+
let verification_result = Drive::verify_identity_id_by_unique_public_key_hash(
113+
proof,
114+
is_proof_subset,
115+
public_key_hash,
116+
);
114117

115118
match verification_result {
116119
Ok((root_hash, maybe_identity_id)) => {
@@ -680,13 +683,13 @@ mod tests {
680683
}
681684

682685
#[test]
683-
fn verify_identity_id_by_public_key_hash() {
686+
fn verify_identity_id_by_unique_public_key_hash() {
684687
let proof = multiple_identity_proof();
685688
let public_key_hash: PublicKeyHash = [
686689
31, 8, 21, 38, 154, 252, 1, 45, 228, 66, 96, 206, 178, 138, 68, 150, 211, 24, 65, 132,
687690
];
688691
let (_root_hash, maybe_identity_id) =
689-
Drive::verify_identity_id_by_public_key_hash(proof, true, public_key_hash)
692+
Drive::verify_identity_id_by_unique_public_key_hash(proof, true, public_key_hash)
690693
.expect("should verify");
691694
let expected_identity_id: [u8; 32] = [
692695
15, 126, 159, 152, 150, 254, 206, 186, 180, 193, 157, 65, 233, 215, 241, 108, 23, 39,

packages/rs-drive/Cargo.toml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true }
5252
intmap = { version = "3.0.1", features = ["serde"], optional = true }
5353
chrono = { version = "0.4.35", optional = true }
5454
itertools = { version = "0.13", optional = true }
55-
grovedb = { git = "https://github.com/dashpay/grovedb", rev= "f89e03e4e0ac12aa2feea5c94b38c09f4909facc", optional = true, default-features = false }
56-
grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev= "f89e03e4e0ac12aa2feea5c94b38c09f4909facc", optional = true }
57-
grovedb-path = { git = "https://github.com/dashpay/grovedb", rev= "f89e03e4e0ac12aa2feea5c94b38c09f4909facc" }
58-
grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev= "f89e03e4e0ac12aa2feea5c94b38c09f4909facc", optional = true }
59-
grovedb-version = { git = "https://github.com/dashpay/grovedb", rev= "f89e03e4e0ac12aa2feea5c94b38c09f4909facc" }
60-
grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev= "f89e03e4e0ac12aa2feea5c94b38c09f4909facc" }
55+
grovedb = { git = "https://github.com/dashpay/grovedb", rev = "f89e03e4e0ac12aa2feea5c94b38c09f4909facc", optional = true, default-features = false }
56+
grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "f89e03e4e0ac12aa2feea5c94b38c09f4909facc", optional = true }
57+
grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "f89e03e4e0ac12aa2feea5c94b38c09f4909facc" }
58+
grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "f89e03e4e0ac12aa2feea5c94b38c09f4909facc", optional = true }
59+
grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "f89e03e4e0ac12aa2feea5c94b38c09f4909facc" }
60+
grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "f89e03e4e0ac12aa2feea5c94b38c09f4909facc" }
6161

6262
[dev-dependencies]
6363
criterion = "0.5"
@@ -85,6 +85,7 @@ name = "benchmarks"
8585
harness = false
8686

8787
[features]
88+
8889
default = ["full", "verify", "fixtures-and-mocks", "cbor_query"]
8990
grovedbg = ["grovedb/grovedbg"]
9091
fee-distribution = ["dpp/fee-distribution"]
@@ -117,4 +118,9 @@ server = [
117118
full = ["server", "ciborium", "serde", "bs58", "tempfile", "base64", "chrono"]
118119
cbor_query = ["ciborium", "dpp/platform-value-cbor", "dpp/cbor"]
119120
grovedb_operations_logging = []
120-
verify = ["grovedb/verify", "grovedb-costs", "dpp/state-transitions", "dpp/system_contracts"]
121+
verify = [
122+
"grovedb/verify",
123+
"grovedb-costs",
124+
"dpp/state-transitions",
125+
"dpp/system_contracts",
126+
]

packages/rs-drive/src/drive/identity/fetch/queries/mod.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use crate::drive::balances::balance_path_vec;
22
use crate::drive::identity::key::fetch::IdentityKeysRequest;
3-
use crate::drive::{
4-
identity_tree_path_vec, non_unique_key_hashes_tree_path, non_unique_key_hashes_tree_path_vec,
5-
unique_key_hashes_tree_path_vec, Drive,
6-
};
3+
use crate::drive::non_unique_key_hashes_tree_path_vec;
4+
use crate::drive::{identity_tree_path_vec, unique_key_hashes_tree_path_vec, Drive};
75
use std::ops::RangeFull;
86

97
use crate::error::Error;
@@ -102,8 +100,9 @@ impl Drive {
102100
let non_unique_key_hashes = non_unique_key_hashes_tree_path_vec();
103101
let mut query = Query::new_single_key(public_key_hash.to_vec());
104102
let sub_query = if let Some(after) = after {
105-
Query::new_single_query_item(QueryItem::RangeFrom(after.to_vec()..))
103+
Query::new_single_query_item(QueryItem::RangeAfter(after.to_vec()..))
106104
} else {
105+
// TODO: why not limit 1?
107106
Query::new_range_full()
108107
};
109108
query.set_subquery(sub_query);

packages/rs-drive/src/drive/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ pub(crate) fn non_unique_key_hashes_tree_path() -> [&'static [u8]; 1] {
261261
}
262262

263263
/// Returns the path to the masternode key hashes.
264-
#[cfg(feature = "server")]
264+
#[cfg(any(feature = "server", feature = "verify"))]
265265
pub(crate) fn non_unique_key_hashes_tree_path_vec() -> Vec<Vec<u8>> {
266266
vec![vec![
267267
RootTree::NonUniquePublicKeyKeyHashesToIdentities as u8,
@@ -278,7 +278,7 @@ pub(crate) fn non_unique_key_hashes_sub_tree_path(public_key_hash: &[u8]) -> [&[
278278
}
279279

280280
/// Returns the path to the masternode key hashes sub tree.
281-
#[cfg(feature = "server")]
281+
#[cfg(any(feature = "server", feature = "verify"))]
282282
pub(crate) fn non_unique_key_hashes_sub_tree_path_vec(public_key_hash: [u8; 20]) -> Vec<Vec<u8>> {
283283
vec![
284284
vec![RootTree::NonUniquePublicKeyKeyHashesToIdentities as u8],

0 commit comments

Comments
 (0)