diff --git a/CHANGELOG.md b/CHANGELOG.md index 2344fc81e9..c28fa1b9d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog ## 1.12.0 - 2019-09-XX -* Minimal Support of Fully-Qualified identifiers: +* Minimal *EXPERIMENTAL* support of Fully-Qualified identifiers: * general format of fully-qualified identifier is `::`. * extended `did_info` parameter of `indy_create_and_store_my_did` function to accepts optional `method_name` filed. This field should be used to create fully qualified DID. * all functions can work with fully-qualified identifiers (new way) as well as with unqualified. @@ -66,7 +66,7 @@ Old `indy_build_get_payment_sources_request` and `indy_parse_get_payment_sources Added correspondent `payment-address sign/verify` commands to Indy CLI. -* Added new *EXPEREMENTAL* functions to get requirements and price for a ledger request. +* Added new *EXPERIMENTAL* functions to get requirements and price for a ledger request. * Libindy `indy_get_request_info` - returns request requirements (with minimal price) correspondent to specific auth rule in case the requester can perform this action. * Libvcx `vcx_get_request_price` - returns request minimal request price for performing an action in case the requester can do it. * Added a set of new Libvcx APIs around credentials and proofs that work with messages that should be exchanged without handling the transport of those messages. diff --git a/docs/migration-guides/migration-guide-1.11.0-1.12.0.md b/docs/migration-guides/migration-guide-1.11.0-1.12.0.md index fff45b957f..a7bf8958d9 100644 --- a/docs/migration-guides/migration-guide-1.11.0-1.12.0.md +++ b/docs/migration-guides/migration-guide-1.11.0-1.12.0.md @@ -48,16 +48,20 @@ This functions also updates all DID related entities stored in the wallet to poi #### Anoncreds API -As we have released Fully-Qualified identifiers, we can work with both identifier formats in a compatible way. +As we have released *EXPERIMENTAL* Fully-Qualified identifiers, we can work with both identifier formats in a compatible way. -The new function [indy_to_unqualified](/~https://github.com/hyperledger/indy-sdk/blob/v1.12.0/libindy/src/api/did.rs#L729) was added. +The new function [indy_to_unqualified](/~https://github.com/hyperledger/indy-sdk/blob/v1.12.0/libindy/src/api/anoncreds.rs#L2378) was added. This function gets unqualified form of a fully-qualified identifier. This function can accept the following entities: * DID * SchemaId * CredentialDefinitionId * RevocationRegistryId -* CredentialOffer +* Schema +* CredentialDefinition +* RevocationRegistryDefinition +* CredentialOffer +* CredentialRequest * ProofRequest Let's consider Credential Issuance and Proof Presentation for different cases. diff --git a/libindy/debian/changelog b/libindy/debian/changelog index 603704be62..773d0d529a 100644 --- a/libindy/debian/changelog +++ b/libindy/debian/changelog @@ -3,7 +3,7 @@ libindy (1.12.0) unstable; urgency=medium [ Hyperledger ] ## 1.12.0 -* Minimal Support of Fully-Qualified identifiers: +* Minimal *EXPERIMENTAL* support of Fully-Qualified identifiers: * extended `did_info` parameter of `indy_create_and_store_my_did` function to accepts optional `method_name` filed. This field should be used to create fully qualified DID. * all functions can work with fully-qualified identifiers (new way) as well as with unqualified. * added a new function -- `indy_to_unqualified` -- that gets unqualified form of a fully qualified identifier. diff --git a/libindy/src/api/anoncreds.rs b/libindy/src/api/anoncreds.rs index 1fb91ea6d4..6adf7ba53b 100644 --- a/libindy/src/api/anoncreds.rs +++ b/libindy/src/api/anoncreds.rs @@ -2389,7 +2389,11 @@ pub extern fn indy_generate_nonce(command_handle: CommandHandle, /// SchemaId /// CredentialDefinitionId /// RevocationRegistryId +/// Schema +/// CredentialDefinition +/// RevocationRegistryDefinition /// CredentialOffer +/// CredentialRequest /// ProofRequest /// /// #Returns diff --git a/libindy/src/domain/anoncreds/credential_definition.rs b/libindy/src/domain/anoncreds/credential_definition.rs index f430ace453..4078e6cd6d 100644 --- a/libindy/src/domain/anoncreds/credential_definition.rs +++ b/libindy/src/domain/anoncreds/credential_definition.rs @@ -77,6 +77,22 @@ pub struct TemporaryCredentialDefinition { pub cred_def_correctness_proof: CredentialDefinitionCorrectnessProof } +impl CredentialDefinition { + pub fn to_unqualified(self) -> CredentialDefinition { + match self { + CredentialDefinition::CredentialDefinitionV1(cred_def) => { + CredentialDefinition::CredentialDefinitionV1(CredentialDefinitionV1 { + id: cred_def.id.to_unqualified(), + schema_id: cred_def.schema_id.to_unqualified(), + signature_type: cred_def.signature_type, + tag: cred_def.tag, + value: cred_def.value, + }) + } + } + } +} + impl From for CredentialDefinitionV1 { fn from(cred_def: CredentialDefinition) -> Self { match cred_def { diff --git a/libindy/src/domain/anoncreds/credential_request.rs b/libindy/src/domain/anoncreds/credential_request.rs index f79fd09f2d..a23040c4c7 100644 --- a/libindy/src/domain/anoncreds/credential_request.rs +++ b/libindy/src/domain/anoncreds/credential_request.rs @@ -26,6 +26,18 @@ pub struct CredentialRequestMetadata { pub master_secret_name: String } +impl CredentialRequest { + pub fn to_unqualified(self) -> CredentialRequest { + CredentialRequest { + prover_did: self.prover_did.to_unqualified(), + cred_def_id: self.cred_def_id.to_unqualified(), + blinded_ms: self.blinded_ms, + blinded_ms_correctness_proof: self.blinded_ms_correctness_proof, + nonce: self.nonce + } + } +} + impl Validatable for CredentialRequest { fn validate(&self) -> Result<(), String> { self.cred_def_id.validate()?; diff --git a/libindy/src/domain/anoncreds/revocation_registry_definition.rs b/libindy/src/domain/anoncreds/revocation_registry_definition.rs index 3592e59311..2e2364e405 100644 --- a/libindy/src/domain/anoncreds/revocation_registry_definition.rs +++ b/libindy/src/domain/anoncreds/revocation_registry_definition.rs @@ -85,6 +85,22 @@ pub enum RevocationRegistryDefinition { RevocationRegistryDefinitionV1(RevocationRegistryDefinitionV1) } +impl RevocationRegistryDefinition { + pub fn to_unqualified(self) -> RevocationRegistryDefinition { + match self { + RevocationRegistryDefinition::RevocationRegistryDefinitionV1(rev_ref_def) => { + RevocationRegistryDefinition::RevocationRegistryDefinitionV1(RevocationRegistryDefinitionV1 { + id: rev_ref_def.id.to_unqualified(), + revoc_def_type: rev_ref_def.revoc_def_type, + tag: rev_ref_def.tag, + cred_def_id: rev_ref_def.cred_def_id.to_unqualified(), + value: rev_ref_def.value, + }) + } + } + } +} + impl From for RevocationRegistryDefinitionV1 { fn from(rev_reg_def: RevocationRegistryDefinition) -> Self { match rev_reg_def { diff --git a/libindy/src/domain/anoncreds/schema.rs b/libindy/src/domain/anoncreds/schema.rs index 4d70ef28cc..be67ee3b74 100644 --- a/libindy/src/domain/anoncreds/schema.rs +++ b/libindy/src/domain/anoncreds/schema.rs @@ -28,6 +28,22 @@ pub enum Schema { SchemaV1(SchemaV1) } +impl Schema { + pub fn to_unqualified(self) -> Schema { + match self { + Schema::SchemaV1(schema) => { + Schema::SchemaV1(SchemaV1 { + id: schema.id.to_unqualified(), + name: schema.name, + version: schema.version, + attr_names: schema.attr_names, + seq_no: schema.seq_no, + }) + } + } + } +} + impl From for SchemaV1 { fn from(schema: Schema) -> Self { match schema { diff --git a/libindy/src/services/anoncreds/helpers.rs b/libindy/src/services/anoncreds/helpers.rs index 464e01df25..7abe372ef6 100644 --- a/libindy/src/services/anoncreds/helpers.rs +++ b/libindy/src/services/anoncreds/helpers.rs @@ -8,7 +8,11 @@ use domain::crypto::did::DidValue; use domain::anoncreds::schema::SchemaId; use domain::anoncreds::credential_definition::CredentialDefinitionId; use domain::anoncreds::revocation_registry_definition::RevocationRegistryId; +use domain::anoncreds::schema::Schema; +use domain::anoncreds::credential_definition::CredentialDefinition; +use domain::anoncreds::revocation_registry_definition::RevocationRegistryDefinition; use domain::anoncreds::credential_offer::CredentialOffer; +use domain::anoncreds::credential_request::CredentialRequest; use domain::anoncreds::proof_request::ProofRequest; use std::collections::{HashSet, HashMap}; @@ -105,42 +109,42 @@ pub fn get_non_revoc_interval(global_interval: &Option, loca interval } -pub fn to_unqualified(entity: &str) -> IndyResult { - info!("to_unqualified >>> entity: {:?}", entity); - - if entity.starts_with(DidValue::PREFIX) { - return Ok(DidValue(entity.to_string()).to_unqualified().0); - } - - if entity.starts_with(SchemaId::PREFIX) { - return Ok(SchemaId(entity.to_string()).to_unqualified().0); - } +macro_rules! _id_to_unqualified { + ($entity:expr, $type_:ident) => ({ + if $entity.starts_with($type_::PREFIX) { + return Ok($type_($entity.to_string()).to_unqualified().0); + } + }) +} - if entity.starts_with(CredentialDefinitionId::PREFIX) { - return Ok(CredentialDefinitionId(entity.to_string()).to_unqualified().0); - } +macro_rules! _object_to_unqualified { + ($entity:expr, $type_:ident) => ({ + if let Ok(object) = ::serde_json::from_str::<$type_>(&$entity) { + return Ok(json!(object.to_unqualified()).to_string()) + } + }) +} - if entity.starts_with(RevocationRegistryId::PREFIX) { - return Ok(RevocationRegistryId(entity.to_string()).to_unqualified().0); - } +pub fn to_unqualified(entity: &str) -> IndyResult { + info!("to_unqualified >>> entity: {:?}", entity); - if let Ok(cred_offer) = ::serde_json::from_str::(&entity) { - let cred_offer = cred_offer.to_unqualified(); - return serde_json::to_string(&cred_offer) - .map_err(|err| IndyError::from_msg(IndyErrorKind::InvalidState, format!("Cannot serialize Credential Offer: {:?}", err))); - } + _id_to_unqualified!(entity, DidValue); + _id_to_unqualified!(entity, SchemaId); + _id_to_unqualified!(entity, CredentialDefinitionId); + _id_to_unqualified!(entity, RevocationRegistryId); - if let Ok(proof_request) = ::serde_json::from_str::(&entity) { - let proof_request = proof_request.to_unqualified(); - return serde_json::to_string(&proof_request) - .map_err(|err| IndyError::from_msg(IndyErrorKind::InvalidState, format!("Cannot serialize Proof Request: {:?}", err))); - } + _object_to_unqualified!(entity, Schema); + _object_to_unqualified!(entity, CredentialDefinition); + _object_to_unqualified!(entity, RevocationRegistryDefinition); + _object_to_unqualified!(entity, CredentialOffer); + _object_to_unqualified!(entity, CredentialRequest); + _object_to_unqualified!(entity, ProofRequest); Ok(entity.to_string()) } #[cfg(test)] -mod tests{ +mod tests { use super::*; fn _interval() -> NonRevocedInterval { NonRevocedInterval { from: None, to: Some(123) } } @@ -165,7 +169,7 @@ mod tests{ mod to_unqualified { use super::*; - + const DID_QUALIFIED: &str = "did:sov:NcYxiDXkpYi6ov5FcYDi1e"; const DID_UNQUALIFIED: &str = "NcYxiDXkpYi6ov5FcYDi1e"; const SCHEMA_ID_QUALIFIED: &str = "schema:sov:did:sov:NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0"; @@ -176,7 +180,7 @@ mod tests{ const REV_REG_ID_UNQUALIFIED: &str = "NcYxiDXkpYi6ov5FcYDi1e:4:NcYxiDXkpYi6ov5FcYDi1e:3:CL:NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0:tag:CL_ACCUM:TAG_1"; const SCHEMA_ID_WITH_SPACES_QUALIFIED: &str = "schema:sov:did:sov:NcYxiDXkpYi6ov5FcYDi1e:2:Passport Schema:1.0"; const SCHEMA_ID_WITH_SPACES_UNQUALIFIED: &str = "NcYxiDXkpYi6ov5FcYDi1e:2:Passport Schema:1.0"; - + #[test] fn test_to_unqualified() { // DID diff --git a/libindy/tests/anoncreds.rs b/libindy/tests/anoncreds.rs index adfdac302c..bac3412956 100644 --- a/libindy/tests/anoncreds.rs +++ b/libindy/tests/anoncreds.rs @@ -3452,14 +3452,57 @@ mod high_cases { mod to_unqualified { use super::*; + use utils::domain::anoncreds::schema::SchemaV1; + use utils::domain::anoncreds::credential_definition::CredentialDefinitionV1; + use utils::domain::anoncreds::credential_offer::CredentialOffer; + use utils::domain::anoncreds::credential_request::CredentialRequest; #[test] - fn to_unqualified() { - let qualified = "did:sov:NcYxiDXkpYi6ov5FcYDi1e"; - let unqualified = "NcYxiDXkpYi6ov5FcYDi1e"; + fn to_unqualified_ids() { + assert_eq!(DID_MY1, anoncreds::to_unqualified(DID_MY1_V1).unwrap()); + assert_eq!(DID_MY1, anoncreds::to_unqualified(DID_MY1).unwrap()); - assert_eq!(unqualified, anoncreds::to_unqualified(qualified).unwrap()); - assert_eq!(unqualified, anoncreds::to_unqualified(unqualified).unwrap()); + assert_eq!(anoncreds::gvt_schema_id(), anoncreds::to_unqualified(&anoncreds::gvt_schema_id_fully_qualified()).unwrap()); + assert_eq!(anoncreds::gvt_cred_def_id(), anoncreds::to_unqualified(&anoncreds::gvt_cred_def_id_fully_qualified()).unwrap()); + assert_eq!(anoncreds::local_gvt_cred_def_id(), anoncreds::to_unqualified(&anoncreds::local_gvt_cred_def_id_fully_qualified()).unwrap()); + } + + #[test] + fn to_unqualified_objects() { + let setup = Setup::wallet(); + + let (schema_id, schema_json) = anoncreds::issuer_create_schema(ISSUER_DID_V1, GVT_SCHEMA_NAME, SCHEMA_VERSION, GVT_SCHEMA_ATTRIBUTES).unwrap(); + + assert_eq!(anoncreds::gvt_schema_id(), anoncreds::to_unqualified(&schema_id).unwrap()); + + let schema_json_un = anoncreds::to_unqualified(&schema_json).unwrap(); + let schema: SchemaV1 = ::serde_json::from_str(&schema_json_un).unwrap(); + assert_eq!(anoncreds::gvt_schema_id(), schema.id.0); + + let (cred_def_id, cred_def_json) = anoncreds::issuer_create_credential_definition(setup.wallet_handle, ISSUER_DID_V1, &schema_json, TAG_1, None, None).unwrap(); + + assert_eq!(anoncreds::local_gvt_cred_def_id(), anoncreds::to_unqualified(&cred_def_id).unwrap()); + + let cred_def_json_un = anoncreds::to_unqualified(&cred_def_json).unwrap(); + let cred_def: CredentialDefinitionV1 = ::serde_json::from_str(&cred_def_json_un).unwrap(); + assert_eq!(anoncreds::local_gvt_cred_def_id(), cred_def.id.0); + assert_eq!(anoncreds::gvt_schema_id(), cred_def.schema_id.0); + + let cred_offer_json = anoncreds::issuer_create_credential_offer(setup.wallet_handle, &cred_def_id).unwrap(); + + let cred_offer_json_un = anoncreds::to_unqualified(&cred_offer_json).unwrap(); + let cred_offer: CredentialOffer = ::serde_json::from_str(&cred_offer_json_un).unwrap(); + assert_eq!(anoncreds::local_gvt_cred_def_id(), cred_offer.cred_def_id.0); + assert_eq!(anoncreds::gvt_schema_id(), cred_offer.schema_id.0); + + anoncreds::prover_create_master_secret(setup.wallet_handle, COMMON_MASTER_SECRET).unwrap(); + + let (cred_req_json, _) = anoncreds::prover_create_credential_req(setup.wallet_handle, DID_MY1_V1, &cred_offer_json, &cred_def_json_un, COMMON_MASTER_SECRET).unwrap(); + + let cred_req_json_un = anoncreds::to_unqualified(&cred_req_json).unwrap(); + let cred_req: CredentialRequest = ::serde_json::from_str(&cred_req_json_un).unwrap(); + assert_eq!(DID_MY1.to_string(), cred_req.prover_did.0); + assert_eq!(anoncreds::local_gvt_cred_def_id(), cred_req.cred_def_id.0); } } } diff --git a/vcx/libvcx/debian/changelog b/vcx/libvcx/debian/changelog index a90a66afc3..a9cc8c5b61 100644 --- a/vcx/libvcx/debian/changelog +++ b/vcx/libvcx/debian/changelog @@ -3,7 +3,8 @@ libvcx (0.4.2) unstable; urgency=medium [ Hyperledger ] ## 0.4.2 -* Extended provisioning config to accept optional `did_method` filed. This field should be used to create fully qualified DIDs. +* *EXPERIMENTAL* + Extended provisioning config to accept optional `did_method` filed. This field should be used to create fully qualified DIDs. The format of identifiers used on CredentialIssuance and ProofPresentation will determine based on the type of remote DID. * Bugfixes diff --git a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h index 6a51408a09..0919cb16a7 100644 --- a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h +++ b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h @@ -1106,12 +1106,16 @@ And is documented in this HIPE: @param entity: utarget entity to disqualify. Can be one of: - Did - SchemaId - CredentialDefinitionId - RevocationRegistryId - CredentialOffer - ProofRequest + Did + SchemaId + CredentialDefinitionId + RevocationRegistryId + Schema + CredentialDefinition + RevocationRegistryDefinition + CredentialOffer + CredentialRequest + ProofRequest Returns entity either in unqualified form or original if casting isn't possible */ diff --git a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java index 19fa4904f2..958678d64a 100644 --- a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java +++ b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java @@ -1555,7 +1555,11 @@ public static CompletableFuture generateNonce() throws IndyException { * SchemaId * CredentialDefinitionId * RevocationRegistryId + * Schema + * CredentialDefinition + * RevocationRegistryDefinition * CredentialOffer + * CredentialRequest * ProofRequest * @return A future that resolves to entity either in unqualified form or original if casting isn't possible * @throws IndyException Thrown if an error occurs when calling the underlying SDK. diff --git a/wrappers/nodejs/README.md b/wrappers/nodejs/README.md index eb3cfe565a..5c589b7ce1 100644 --- a/wrappers/nodejs/README.md +++ b/wrappers/nodejs/README.md @@ -931,7 +931,7 @@ This function should be used to the proper casting of fully qualified entity to 2) Verifier prepares a Proof Request based on fully qualified identifiers or Prover, which doesn't support fully qualified identifiers. 3) another case when casting to unqualified form needed -* `entity`: String - target entity to disqualify. Can be one of: Did, SchemaId, CredentialDefinitionId, RevocationRegistryId, CredentialOffer. +* `entity`: String - target entity to disqualify. Can be one of: Did, SchemaId, CredentialDefinitionId, RevocationRegistryId, Schema, CredentialDefinition, RevocationRegistryDefinition, CredentialOffer, CredentialRequest, ProofRequest. * __->__ `res`: Json - entity either in unqualified form or original if casting isn't possible ### blob_storage diff --git a/wrappers/python/indy/anoncreds.py b/wrappers/python/indy/anoncreds.py index 585f8d40c1..547b1eebff 100644 --- a/wrappers/python/indy/anoncreds.py +++ b/wrappers/python/indy/anoncreds.py @@ -1873,7 +1873,11 @@ async def to_unqualified(entity: str) -> str: SchemaId CredentialDefinitionId RevocationRegistryId + Schema + CredentialDefinition + RevocationRegistryDefinition CredentialOffer + CredentialRequest ProofRequest :return: entity either in unqualified form or original if casting isn't possible diff --git a/wrappers/rust/src/anoncreds.rs b/wrappers/rust/src/anoncreds.rs index 6c5a73c2e0..fe1a6bf235 100644 --- a/wrappers/rust/src/anoncreds.rs +++ b/wrappers/rust/src/anoncreds.rs @@ -1453,12 +1453,16 @@ fn _generate_nonce(command_handle: CommandHandle, cb: Option) /// /// # Arguments /// * `entity`: target entity to disqualify. Can be one of: -/// Did -/// SchemaId -/// CredentialDefinitionId -/// RevocationRegistryId -/// CredentialOffer -/// ProofRequest +/// Did +/// SchemaId +/// CredentialDefinitionId +/// RevocationRegistryId +/// Schema +/// CredentialDefinition +/// RevocationRegistryDefinition +/// CredentialOffer +/// CredentialRequest +/// ProofRequest /// /// # Returns /// * `res`: entity either in unqualified form or original if casting isn't possible