From cf4386955c21021f9f0c54436d6aedb8911ebc79 Mon Sep 17 00:00:00 2001 From: George Mulhearn <57472912+gmulhearn@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:43:48 +1000 Subject: [PATCH] (fix) Return IndyVdr based CredentialDefinition schemaId as full schemaId (not sequence number) (#1313) --- aries/aries_vcx/tests/test_primitives.rs | 18 +++++- .../src/ledger/indy_vdr_ledger.rs | 58 ++++++++++++++++++- .../src/ledger/type_conversion.rs | 39 ++++++------- .../src/domain/constants.rs | 1 + .../src/domain/mod.rs | 1 + .../src/domain/txn.rs | 19 ++++++ .../indy_ledger_response_parser/src/lib.rs | 18 +++++- 7 files changed, 128 insertions(+), 26 deletions(-) create mode 100644 aries/misc/indy_ledger_response_parser/src/domain/txn.rs diff --git a/aries/aries_vcx/tests/test_primitives.rs b/aries/aries_vcx/tests/test_primitives.rs index 2ce5bb075b..dc8a65d7c7 100644 --- a/aries/aries_vcx/tests/test_primitives.rs +++ b/aries/aries_vcx/tests/test_primitives.rs @@ -1,5 +1,8 @@ use std::error::Error; +use anoncreds_types::{ + data_types::ledger::cred_def::CredentialDefinition, utils::validation::Validatable, +}; use aries_vcx::common::primitives::{ credential_definition::generate_cred_def, revocation_registry::generate_rev_reg, }; @@ -46,13 +49,24 @@ async fn test_pool_create_cred_def_real() -> Result<(), Box> { .publish_cred_def(&setup.wallet, cred_def.try_clone()?, &setup.institution_did) .await?; - std::thread::sleep(std::time::Duration::from_secs(2)); + tokio::time::sleep(std::time::Duration::from_secs(2)).await; let cred_def_json_ledger = ledger_read .get_cred_def(&cred_def.id, Some(&setup.institution_did)) .await?; + cred_def_json_ledger.validate()?; + + // same as the original generated cred def, but schema ID corrected to the qualified version + let cred_def_corrected_schema_id = CredentialDefinition { + schema_id: schema.schema_id, + ..cred_def.try_clone().unwrap() + }; - assert!(serde_json::to_string(&cred_def_json_ledger)?.contains(&cred_def.id.to_string())); + // check cred def matches originally, but with corected schema ID. + assert_eq!( + serde_json::to_value(cred_def_json_ledger)?, + serde_json::to_value(cred_def_corrected_schema_id)? + ); Ok(()) } diff --git a/aries/aries_vcx_ledger/src/ledger/indy_vdr_ledger.rs b/aries/aries_vcx_ledger/src/ledger/indy_vdr_ledger.rs index 4e55fea7d5..2bfbd63141 100644 --- a/aries/aries_vcx_ledger/src/ledger/indy_vdr_ledger.rs +++ b/aries/aries_vcx_ledger/src/ledger/indy_vdr_ledger.rs @@ -22,10 +22,17 @@ pub use indy_ledger_response_parser::GetTxnAuthorAgreementData; use indy_ledger_response_parser::{ ResponseParser, RevocationRegistryDeltaInfo, RevocationRegistryInfo, }; -use indy_vdr as vdr; +use indy_vdr::{ + self as vdr, + ledger::{ + identifiers::SchemaId as IndyVdrSchemaId, + requests::cred_def::CredentialDefinition as IndyVdrCredentialDefinition, + }, + utils::{did::DidValue, Validatable}, +}; use log::{debug, trace}; use public_key::Key; -use serde_json::Value; +use serde_json::{json, Value}; use time::OffsetDateTime; use vdr::{ config::PoolConfig, @@ -162,6 +169,40 @@ where trace!("submit_request << ledger response (is from cache: {is_from_cache}): {response}"); Ok(response) } + + async fn resolve_schema_id_from_seq_no( + &self, + seq_no: i32, + submitter_did: Option<&Did>, + ) -> VcxLedgerResult { + let response = self.get_ledger_txn(seq_no, submitter_did).await?; + let mut txn_response = self.response_parser.parse_get_txn_response(&response)?; + let txn = txn_response["txn"].take(); + + // mimic acapy & credo-ts behaviour - assumes node protocol >= 1.4 + + // check correct tx + if txn["type"] != json!("101") { + return Err(VcxLedgerError::InvalidLedgerResponse); + } + + // pull schema identifier parts + let schema_did = &txn["metadata"]["from"]; + let schema_name = &txn["data"]["data"]["name"]; + let schema_version = &txn["data"]["data"]["version"]; + let (Value::String(did), Value::String(name), Value::String(ver)) = + (schema_did, schema_name, schema_version) + else { + return Err(VcxLedgerError::InvalidLedgerResponse); + }; + + // construct indy schema ID from parts + let did = DidValue::new(did, None); + did.validate()?; + let schema_id = IndyVdrSchemaId::new(&did, name, ver); + schema_id.validate()?; + Ok(schema_id) + } } impl IndyVdrLedgerWrite @@ -487,7 +528,18 @@ where let cred_def = self .response_parser .parse_get_cred_def_response(&response, None)?; - Ok(cred_def.convert(())?) + + // extract and map seqNo -> schemaId if required + let IndyVdrCredentialDefinition::CredentialDefinitionV1(mut cred_def) = cred_def; + if let Ok(seq_no) = cred_def.schema_id.0.parse::() { + cred_def.schema_id = self + .resolve_schema_id_from_seq_no(seq_no, submitter_did) + .await?; + } + + let cred_def = IndyVdrCredentialDefinition::CredentialDefinitionV1(cred_def).convert(())?; + + Ok(cred_def) } async fn get_rev_reg_def_json( diff --git a/aries/aries_vcx_ledger/src/ledger/type_conversion.rs b/aries/aries_vcx_ledger/src/ledger/type_conversion.rs index 403bd31d8f..e0f30ffa1d 100644 --- a/aries/aries_vcx_ledger/src/ledger/type_conversion.rs +++ b/aries/aries_vcx_ledger/src/ledger/type_conversion.rs @@ -145,26 +145,25 @@ impl Convert for IndyVdrCredentialDefinition { fn convert(self, (): Self::Args) -> Result { match self { IndyVdrCredentialDefinition::CredentialDefinitionV1(cred_def) => { - if let Some((_method, issuer_id, _sig_type, _schema_id, _tag)) = cred_def.id.parts() - { - Ok(OurCredentialDefinition { - id: OurCredentialDefinitionId::new(cred_def.id.to_string())?, - schema_id: OurSchemaId::new_unchecked(cred_def.schema_id.to_string()), - signature_type: OurSignatureType::CL, - tag: cred_def.tag, - value: OurCredentialDefinitionData { - primary: serde_json::from_value(cred_def.value.primary)?, - revocation: cred_def - .value - .revocation - .map(serde_json::from_value) - .transpose()?, - }, - issuer_id: IssuerId::new(issuer_id.to_string())?, - }) - } else { - todo!() - } + let id = &cred_def.id; + let Some((_method, issuer_id, _sig_type, _schema_id, _tag)) = id.parts() else { + return Err(format!("cred def ID is malformed. cannot convert. {}", id).into()); + }; + Ok(OurCredentialDefinition { + id: OurCredentialDefinitionId::new(id.to_string())?, + schema_id: OurSchemaId::new(cred_def.schema_id.to_string())?, + signature_type: OurSignatureType::CL, + tag: cred_def.tag, + value: OurCredentialDefinitionData { + primary: serde_json::from_value(cred_def.value.primary)?, + revocation: cred_def + .value + .revocation + .map(serde_json::from_value) + .transpose()?, + }, + issuer_id: IssuerId::new(issuer_id.to_string())?, + }) } } } diff --git a/aries/misc/indy_ledger_response_parser/src/domain/constants.rs b/aries/misc/indy_ledger_response_parser/src/domain/constants.rs index 0f2774d06c..66889bdf20 100644 --- a/aries/misc/indy_ledger_response_parser/src/domain/constants.rs +++ b/aries/misc/indy_ledger_response_parser/src/domain/constants.rs @@ -5,3 +5,4 @@ pub const GET_REVOC_REG_DEF: &str = "115"; pub const GET_REVOC_REG: &str = "116"; pub const GET_REVOC_REG_DELTA: &str = "117"; pub const GET_TXN_AUTHR_AGRMT: &str = "6"; +pub const GET_TXN: &str = "3"; diff --git a/aries/misc/indy_ledger_response_parser/src/domain/mod.rs b/aries/misc/indy_ledger_response_parser/src/domain/mod.rs index 0857d17f4f..577bfc467e 100644 --- a/aries/misc/indy_ledger_response_parser/src/domain/mod.rs +++ b/aries/misc/indy_ledger_response_parser/src/domain/mod.rs @@ -7,3 +7,4 @@ pub mod response; pub mod rev_reg; pub mod rev_reg_def; pub mod schema; +pub mod txn; diff --git a/aries/misc/indy_ledger_response_parser/src/domain/txn.rs b/aries/misc/indy_ledger_response_parser/src/domain/txn.rs new file mode 100644 index 0000000000..4f8929fe0f --- /dev/null +++ b/aries/misc/indy_ledger_response_parser/src/domain/txn.rs @@ -0,0 +1,19 @@ +use serde_json::Value; + +use super::{ + constants::GET_TXN, + response::{GetReplyResultV0, GetReplyResultV1, ReplyType}, +}; + +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum GetTxnReplyResult { + GetTxnReplyResultV0(GetReplyResultV0), + GetTxnReplyResultV1(GetReplyResultV1), +} + +impl ReplyType for GetTxnReplyResult { + fn get_type<'a>() -> &'a str { + GET_TXN + } +} diff --git a/aries/misc/indy_ledger_response_parser/src/lib.rs b/aries/misc/indy_ledger_response_parser/src/lib.rs index 44cf841e82..49ec2feee7 100644 --- a/aries/misc/indy_ledger_response_parser/src/lib.rs +++ b/aries/misc/indy_ledger_response_parser/src/lib.rs @@ -8,7 +8,7 @@ pub mod error; use anoncreds_clsignatures::RevocationRegistryDelta as ClRevocationRegistryDelta; pub use domain::author_agreement::GetTxnAuthorAgreementData; -use domain::author_agreement::GetTxnAuthorAgreementResult; +use domain::{author_agreement::GetTxnAuthorAgreementResult, txn::GetTxnReplyResult}; use error::LedgerResponseParserError; use indy_vdr::{ ledger::{ @@ -236,6 +236,22 @@ impl ResponseParser { }) } + // https://github.com/hyperledger/indy-node/blob/main/docs/source/requests.md#get_txn + pub fn parse_get_txn_response( + &self, + get_txn_response: &str, + ) -> Result { + let reply: Reply = Self::parse_response(get_txn_response)?; + + let data = match reply.result() { + GetTxnReplyResult::GetTxnReplyResultV0(res) => { + res.data.unwrap_or(serde_json::Value::Null) + } + GetTxnReplyResult::GetTxnReplyResultV1(res) => res.txn.data, + }; + Ok(data) + } + pub fn parse_response(response: &str) -> Result, LedgerResponseParserError> where T: DeserializeOwned + ReplyType + ::std::fmt::Debug,