diff --git a/agents/node/vcxagent-core/src/services/service-prover.js b/agents/node/vcxagent-core/src/services/service-prover.js index ac7c1b3011..c65b848e16 100644 --- a/agents/node/vcxagent-core/src/services/service-prover.js +++ b/agents/node/vcxagent-core/src/services/service-prover.js @@ -55,7 +55,8 @@ module.exports.createServiceProver = function createServiceProver ({ logger, loa } async function buildDisclosedProof (disclosedProofId, proofRequest) { - const disclosedProof = await DisclosedProof.create({ sourceId: 'proof', request: JSON.stringify(proofRequest) }) + const request = typeof proofRequest === 'string' ? proofRequest : JSON.stringify(proofRequest) + const disclosedProof = await DisclosedProof.create({ sourceId: 'proof', request }) await saveDisclosedProof(disclosedProofId, disclosedProof) } diff --git a/agents/node/vcxagent-core/src/services/service-verifier.js b/agents/node/vcxagent-core/src/services/service-verifier.js index 96590a8987..c6782828b3 100644 --- a/agents/node/vcxagent-core/src/services/service-verifier.js +++ b/agents/node/vcxagent-core/src/services/service-verifier.js @@ -13,12 +13,13 @@ module.exports.createServiceVerifier = function createServiceVerifier ({ logger, } async function sendProofRequest (connectionId, proofId) { + logger.debug(`Verifier sending proof request proofId=${proofId}, connectionId=${connectionId}`) const connection = await loadConnection(connectionId) const proof = await loadProof(proofId) await proof.requestProof(connection) const state = await proof.getState() await saveProof(proofId, proof) - const proofRequestMessage = proof.getProofRequestMessage() + const proofRequestMessage = await proof.getProofRequestMessage() return { state, proofRequestMessage } } @@ -35,6 +36,12 @@ module.exports.createServiceVerifier = function createServiceVerifier ({ logger, return await proof.getState() } + async function getProofState (proofId) { + const proof = await loadProof(proofId) + const { proofState } = await proof.getProof() + return proofState + } + async function listIds () { return listProofIds() } @@ -59,6 +66,7 @@ module.exports.createServiceVerifier = function createServiceVerifier ({ logger, listIds, printInfo, - getState + getState, + getProofState } } diff --git a/libvcx/src/api/credential_def.rs b/libvcx/src/api/credential_def.rs index 2121c3fa09..ef53508d6c 100644 --- a/libvcx/src/api/credential_def.rs +++ b/libvcx/src/api/credential_def.rs @@ -29,8 +29,16 @@ use crate::utils::threadpool::spawn; /// TODO: Currently supports ISSUANCE BY DEFAULT, support for ISSUANCE ON DEMAND will be added as part of ticket: IS-1074 /// support_revocation: true|false - Optional, by default its false /// tails_file: path to tails file - Optional if support_revocation is false +/// tails_url: URL where the holder can download the tails file - Optional if support_revocation is false +/// tails_base_url: incomplete URL where the holder can download the tails file - Optional if support_revocation is false /// max_creds: size of tails file - Optional if support_revocation is false -/// # Examples config -> "{}" | "{"support_revocation":false}" | "{"support_revocation":true, "tails_file": "/tmp/tailsfile.txt", "max_creds": 1}" +/// If tails_location is specified, the exact value is written to the ledger and obtainable via vcx_credential_get_tails_location. +/// If tails_base_location in specified, the value written to the ledger and obtainable via vcx_credential_get_tails_location is "{tails_base_location}/{tails_hash}". +/// It is not allowed to specify both tails_location and tails_base_location. +/// # Examples config -> "{}" +/// | "{"support_revocation":false}" +/// | "{"support_revocation":true, "tails_file": "/tmp/tailsfile.txt", "max_creds": 1, "tails_url": "https://dummy.faber.org/DvVhi9j4a3RYdZoQxBerhUUHnyBf8k4j8a5Zp2vgLHpW"}" +/// | "{"support_revocation":true, "tails_file": "/tmp/tailsfile.txt", "max_creds": 1, "tails_base_url": "https://dummy.faber.org"}" /// cb: Callback that provides CredentialDef handle and error status of request. /// /// payment_handle: future use (currently uses any address in wallet) diff --git a/libvcx/src/aries/messages/proof_presentation/presentation_request.rs b/libvcx/src/aries/messages/proof_presentation/presentation_request.rs index d3a1bbd8a9..abbd65262b 100644 --- a/libvcx/src/aries/messages/proof_presentation/presentation_request.rs +++ b/libvcx/src/aries/messages/proof_presentation/presentation_request.rs @@ -1,7 +1,6 @@ use crate::error::prelude::*; use crate::aries::messages::a2a::{A2AMessage, MessageId}; use crate::aries::messages::attachment::{AttachmentId, Attachments}; -use crate::aries::messages::connection::service::Service; use crate::libindy::proofs::proof_request::ProofRequestData; #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Default)] @@ -30,6 +29,7 @@ impl PresentationRequest { } pub fn set_request_presentations_attach(mut self, request_presentations: &PresentationRequestData) -> VcxResult { + trace!("set_request_presentations_attach >> {:?}", request_presentations); self.request_presentations_attach.add_base64_encoded_json_attachment(AttachmentId::PresentationRequest, json!(request_presentations))?; Ok(self) } diff --git a/libvcx/src/credential_def.rs b/libvcx/src/credential_def.rs index 63d1de88c7..0d3460e0e9 100644 --- a/libvcx/src/credential_def.rs +++ b/libvcx/src/credential_def.rs @@ -39,11 +39,12 @@ pub struct CredentialDef { state: PublicEntityStateType, } -#[derive(Deserialize, Debug, Serialize)] +#[derive(Clone, Deserialize, Debug, Serialize)] pub struct RevocationDetails { pub support_revocation: Option, pub tails_file: Option, pub tails_url: Option, + pub tails_base_url: Option, pub max_creds: Option, } @@ -68,12 +69,20 @@ pub struct RevocationRegistryDefinition { pub ver: String, } -fn _replace_tails_location(new_rev_reg_def: &str, tails_url: &str) -> VcxResult { - trace!("_replace_tails_location >>> new_rev_reg_def: {}, tails_url: {}", new_rev_reg_def, tails_url); +fn _replace_tails_location(new_rev_reg_def: &str, revocation_details: &RevocationDetails) -> VcxResult { + trace!("_replace_tails_location >>> new_rev_reg_def: {}, revocation_details: {:?}", new_rev_reg_def, revocation_details); let mut new_rev_reg_def: RevocationRegistryDefinition = serde_json::from_str(new_rev_reg_def) .map_err(|err| VcxError::from_msg(VcxErrorKind::SerializationError, format!("Failed to deserialize new rev_reg_def: {:?}, error: {:?}", new_rev_reg_def, err)))?; - new_rev_reg_def.value.tails_location = String::from(tails_url); + let tails_location = match &revocation_details.tails_url { + Some(tails_url) => tails_url.to_string(), + None => match &revocation_details.tails_base_url { + Some(tails_base_url) => vec![tails_base_url.to_string(), new_rev_reg_def.value.tails_hash.to_owned()].join("/"), + None => return Err(VcxError::from_msg(VcxErrorKind::InvalidRevocationDetails, "Both tails_url and tails_base_location not found in revocation details")) + } + }; + + new_rev_reg_def.value.tails_location = String::from(tails_location); serde_json::to_string(&new_rev_reg_def) .map_err(|err| VcxError::from_msg(VcxErrorKind::SerializationError, format!("Failed to serialize new rev_reg_def: {:?}, error: {:?}", new_rev_reg_def, err))) @@ -170,13 +179,13 @@ impl CredentialDef { fn get_state(&self) -> u32 { self.state as u32 } fn rotate_rev_reg(&mut self, revocation_details: &str) -> VcxResult { - debug!("rotate_rev_reg >>>"); + debug!("rotate_rev_reg >>> revocation_details: {}", revocation_details); let revocation_details = _parse_revocation_details(revocation_details)?; - let (tails_url, tails_file, max_creds, issuer_did) = ( - revocation_details.tails_url.ok_or(VcxError::from_msg(VcxErrorKind::InvalidRevocationDetails, "tails_url not found in revocation details"))?, - revocation_details.tails_file.or(self.get_tails_file()), + let (tails_file, max_creds, issuer_did) = ( + revocation_details.clone().tails_file.or(self.get_tails_file()), revocation_details.max_creds.or(self.get_max_creds()), - self.issuer_did.as_ref()); + self.issuer_did.as_ref() + ); match (&mut self.rev_reg, &tails_file, &max_creds, &issuer_did) { (Some(rev_reg), Some(tails_file), Some(max_creds), Some(issuer_did)) => { let tag = format!("tag{}", rev_reg.tag + 1); @@ -184,7 +193,7 @@ impl CredentialDef { anoncreds::generate_rev_reg(&issuer_did, &self.id, &tails_file, *max_creds, tag.as_str()) .map_err(|err| err.map(VcxErrorKind::CreateRevRegDef, "Cannot create revocation registry defintion"))?; - let new_rev_reg_def = _replace_tails_location(&rev_reg_def, &tails_url)?; + let new_rev_reg_def = _replace_tails_location(&rev_reg_def, &revocation_details)?; let rev_reg_def_payment_txn = anoncreds::publish_rev_reg_def(&issuer_did, &new_rev_reg_def) .map_err(|err| err.map(VcxErrorKind::CreateCredDef, "Cannot publish revocation registry defintion"))?; @@ -213,16 +222,23 @@ impl CredentialDef { } fn _parse_revocation_details(revocation_details: &str) -> VcxResult { - serde_json::from_str::(&revocation_details) - .to_vcx(VcxErrorKind::InvalidRevocationDetails, "Cannot deserialize RevocationDetails") + let revoc_details = serde_json::from_str::(&revocation_details) + .to_vcx(VcxErrorKind::InvalidRevocationDetails, "Cannot deserialize RevocationDetails")?; + + match revoc_details.tails_url.is_some() && revoc_details.tails_base_url.is_some() { + true => Err(VcxError::from_msg(VcxErrorKind::InvalidOption, "It is allowed to specify either tails_location or tails_base_location, but not both")), + false => Ok(revoc_details) + } } fn _maybe_set_url(rev_reg_def_json: &str, revocation_details: &RevocationDetails) -> VcxResult { - let mut rev_reg_def: serde_json::Value = serde_json::from_str(&rev_reg_def_json) + let mut rev_reg_def: RevocationRegistryDefinition = serde_json::from_str(&rev_reg_def_json) .map_err(|err| VcxError::from_msg(VcxErrorKind::InvalidJson, format!("Invalid RevocationRegistryDefinition: {:?}, err: {:?}", rev_reg_def_json, err)))?; if let Some(tails_url) = &revocation_details.tails_url { - rev_reg_def["value"]["tailsLocation"] = serde_json::Value::String(tails_url.to_string()); + rev_reg_def.value.tails_location = tails_url.to_string(); + } else if let Some(tails_base_url) = &revocation_details.tails_base_url { + rev_reg_def.value.tails_location = vec![tails_base_url.to_string(), rev_reg_def.value.tails_hash.to_owned()].join("/") } serde_json::to_string(&rev_reg_def) @@ -670,6 +686,28 @@ pub mod tests { assert_eq!(rev_reg_def["value"]["tailsLocation"], utils::constants::TEST_TAILS_URL.to_string()); } + #[cfg(feature = "pool_tests")] + #[test] + fn test_tails_base_url_written_to_ledger() { + let _setup = SetupLibraryWalletPoolZeroFees::init(); + let tails_url = utils::constants::TEST_TAILS_URL.to_string(); + + let (schema_id, _) = libindy::utils::anoncreds::tests::create_and_write_test_schema(utils::constants::DEFAULT_SCHEMA_ATTRS); + let did = settings::get_config_value(settings::CONFIG_INSTITUTION_DID).unwrap(); + + let revocation_details = json!({"support_revocation": true, "tails_file": get_temp_dir_path("tails.txt").to_str().unwrap(), "max_creds": 2, "tails_base_url": tails_url}).to_string(); + let handle = create_and_publish_credentialdef("1".to_string(), + "test_tails_url_written_to_ledger".to_string(), + did, + schema_id, + "tag1".to_string(), + revocation_details).unwrap(); + let rev_reg_def = get_rev_reg_def(handle).unwrap().unwrap(); + let rev_reg_def: serde_json::Value = serde_json::from_str(&rev_reg_def).unwrap(); + let tails_hash = get_tails_hash(handle).unwrap(); + assert_eq!(rev_reg_def["value"]["tailsLocation"], vec![tails_url, tails_hash].join("/")); + } + #[cfg(feature = "pool_tests")] #[test] fn test_create_revocable_cred_def_with_payments() { diff --git a/libvcx/src/disclosed_proof.rs b/libvcx/src/disclosed_proof.rs index b9a366503e..672b9c1d5a 100644 --- a/libvcx/src/disclosed_proof.rs +++ b/libvcx/src/disclosed_proof.rs @@ -40,7 +40,7 @@ pub fn create_proof(source_id: &str, proof_req: &str) -> VcxResult { let presentation_request: PresentationRequest = serde_json::from_str(proof_req) .map_err(|err| VcxError::from_msg(VcxErrorKind::InvalidJson, - format!("Strict `aries` protocol is enabled. Can not parse `aries` formatted Presentation Request: {}", err)))?; + format!("Strict `aries` protocol is enabled. Can not parse `aries` formatted Presentation Request: {}\nError: {}", proof_req, err)))?; let proof = Prover::create(source_id, presentation_request)?; HANDLE_MAP.add(proof) @@ -55,7 +55,7 @@ pub fn create_proof_with_msgid(source_id: &str, connection_handle: u32, msg_id: let presentation_request: PresentationRequest = serde_json::from_str(&proof_request) .map_err(|err| VcxError::from_msg(VcxErrorKind::InvalidJson, - format!("Strict `aries` protocol is enabled. Can not parse `aries` formatted Presentation Request: {}", err)))?; + format!("Strict `aries` protocol is enabled. Can not parse `aries` formatted Presentation Request: {}\nError: {}", proof_request, err)))?; let proof = Prover::create(source_id, presentation_request)?; diff --git a/libvcx/src/libindy/utils/wallet.rs b/libvcx/src/libindy/utils/wallet.rs index 36a1a2543d..86694a4ef2 100644 --- a/libvcx/src/libindy/utils/wallet.rs +++ b/libvcx/src/libindy/utils/wallet.rs @@ -14,7 +14,7 @@ struct WalletConfig { wallet_key_derivation: String, wallet_type: Option, storage_config: Option, - storage_credentials: Option, + storage_credentials: Option, rekey: Option, rekey_derivation_method: Option } @@ -25,7 +25,7 @@ struct WalletCredentials { #[serde(skip_serializing_if = "Option::is_none")] rekey: Option, #[serde(skip_serializing_if = "Option::is_none")] - storage_credentials: Option, + storage_credentials: Option, key_derivation_method: String, #[serde(skip_serializing_if = "Option::is_none")] rekey_derivation_method: Option @@ -83,7 +83,7 @@ pub fn create_wallet_from_config(config: &str) -> VcxResult<()> { &config.wallet_key_derivation, config.wallet_type.as_ref().map(String::as_str), config.storage_config.as_ref().map(String::as_str), - config.storage_credentials.as_ref().map(String::as_str), + config.storage_credentials.as_ref().map(|s| s.to_string()).as_deref(), )?; trace!("Wallet with handle {:?} and config {:?} created", wh, config); @@ -121,7 +121,7 @@ pub fn build_wallet_credentials(key: &str, storage_credentials: Option<&str>, ke serde_json::to_string(&WalletCredentials { key: String::from(key), rekey: rekey.map(|s| s.into()), - storage_credentials: storage_credentials.map(|s| s.into()), + storage_credentials: storage_credentials.map(|s| serde_json::from_str(s).unwrap()), key_derivation_method: String::from(key_derivation_method), rekey_derivation_method: rekey_derivation_method.map(|s| s.into()) }).map_err(|err| VcxError::from_msg(VcxErrorKind::SerializationError, format!("Failed to serialize WalletCredentials, err: {:?}", err))) @@ -165,7 +165,7 @@ pub fn create_and_open_as_main_wallet(wallet_name: &str, wallet_key: &str, key_d pub fn open_wallet_directly(wallet_config: &str) -> VcxResult { let config: WalletConfig = serde_json::from_str(wallet_config) .map_err(|err| VcxError::from_msg(VcxErrorKind::InvalidJson, format!("Cannot deserialize WalletConfig {:?}, err: {:?}", wallet_config, err)))?; - open_as_main_wallet(&config.wallet_name, &config.wallet_key, &config.wallet_key_derivation, config.wallet_type.as_deref(), config.storage_config.as_deref(), config.storage_credentials.as_deref(), config.rekey.as_deref(), config.rekey_derivation_method.as_deref()) + open_as_main_wallet(&config.wallet_name, &config.wallet_key, &config.wallet_key_derivation, config.wallet_type.as_deref(), config.storage_config.as_deref(), config.storage_credentials.as_ref().map(|s| s.to_string()).as_deref(), config.rekey.as_deref(), config.rekey_derivation_method.as_deref()) } pub fn close_main_wallet() -> VcxResult<()> { diff --git a/wrappers/node/src/api/credential-def.ts b/wrappers/node/src/api/credential-def.ts index 912ef665e2..d3849398fd 100644 --- a/wrappers/node/src/api/credential-def.ts +++ b/wrappers/node/src/api/credential-def.ts @@ -73,6 +73,7 @@ export interface IRevocationDetails { supportRevocation?: boolean; tailsFile?: string; tailsUrl?: string; + tailsBaseUrl?: string; } export enum CredentialDefState { @@ -120,8 +121,8 @@ export class CredentialDef extends VCXBase { support_revocation: revocationDetails.supportRevocation, tails_file: revocationDetails.tailsFile, tails_url: revocationDetails.tailsUrl, + tails_base_url: revocationDetails.tailsBaseUrl, }; - try { await credentialDef._create((cb) => rustAPI().vcx_credentialdef_create( @@ -176,6 +177,7 @@ export class CredentialDef extends VCXBase { support_revocation: revocationDetails.supportRevocation, tails_file: revocationDetails.tailsFile, tails_url: revocationDetails.tailsUrl, + tails_base_url: revocationDetails.tailsBaseUrl, }; const credDefForEndorser = await createFFICallbackPromise<{ credDefTxn: string; @@ -472,6 +474,7 @@ export class CredentialDef extends VCXBase { max_creds: revocationDetails.maxCreds, tails_file: revocationDetails.tailsFile, tails_url: revocationDetails.tailsUrl, + tails_base_url: revocationDetails.tailsBaseUrl, }; try { const dataStr = await createFFICallbackPromise( diff --git a/wrappers/node/src/api/proof.ts b/wrappers/node/src/api/proof.ts index c8a1383a58..0c04aa9491 100644 --- a/wrappers/node/src/api/proof.ts +++ b/wrappers/node/src/api/proof.ts @@ -265,7 +265,7 @@ export class Proof extends VCXBaseWithState { commandHandle, proof.sourceId, JSON.stringify(createDataRest.attrs), - JSON.stringify(createDataRest.preds), + JSON.stringify(createDataRest.preds || []), JSON.stringify(createDataRest.revocationInterval), createDataRest.name, cb,