diff --git a/libvcx/src/api/credential.rs b/libvcx/src/api/credential.rs index 160646bf7a..76d6d4db71 100644 --- a/libvcx/src/api/credential.rs +++ b/libvcx/src/api/credential.rs @@ -272,6 +272,43 @@ pub extern fn vcx_credential_get_attributes(command_handle: CommandHandle, error::SUCCESS.code_num } +#[no_mangle] +pub extern fn vcx_credential_get_attachment(command_handle: CommandHandle, + credential_handle: u32, + cb: Option) -> u32 { + info!("vcx_credential_get_attachment >>> credential_handle: {:?}", credential_handle); + + check_useful_c_callback!(cb, VcxErrorKind::InvalidOption); + if !credential::is_valid_handle(credential_handle) { + return VcxError::from(VcxErrorKind::InvalidCredentialHandle).into(); + } + + let source_id = credential::get_source_id(credential_handle).unwrap_or_default(); + trace!("vcx_credential_get_attachment(command_handle: {}, credential_handle: {}) source_id: {})", + command_handle, credential_handle, source_id); + + spawn(move || { + match credential::get_attachment(credential_handle) { + Ok(s) => { + trace!("vcx_credential_get_attachment_cb(commmand_handle: {}, rc: {}, attachment: {}) source_id: {}", + command_handle, error::SUCCESS.code_num, s, source_id); + let attach = CStringUtils::string_to_cstring(s); + cb(command_handle, error::SUCCESS.code_num, attach.as_ptr()); + } + Err(e) => { + error!("vcx_credential_get_attachment_cb(commmand_handle: {}, rc: {}, attachment: {}) source_id: {}", + command_handle, e, "".to_string(), source_id); + cb(command_handle, e.into(), ptr::null_mut()); + } + }; + + Ok(()) + }); + + error::SUCCESS.code_num +} + + /// Create a Credential object based off of a known message id for a given connection. /// /// #Params diff --git a/libvcx/src/aries/handlers/issuance/holder/holder.rs b/libvcx/src/aries/handlers/issuance/holder/holder.rs index 754307b412..489a66aff9 100644 --- a/libvcx/src/aries/handlers/issuance/holder/holder.rs +++ b/libvcx/src/aries/handlers/issuance/holder/holder.rs @@ -55,6 +55,10 @@ impl Holder { self.holder_sm.get_attributes() } + pub fn get_attachment(&self) -> VcxResult { + self.holder_sm.get_attachment() + } + pub fn delete_credential(&self) -> VcxResult<()> { self.holder_sm.delete_credential() } diff --git a/libvcx/src/aries/handlers/issuance/holder/state_machine.rs b/libvcx/src/aries/handlers/issuance/holder/state_machine.rs index 55d4308b36..2cf54cb8a9 100644 --- a/libvcx/src/aries/handlers/issuance/holder/state_machine.rs +++ b/libvcx/src/aries/handlers/issuance/holder/state_machine.rs @@ -219,6 +219,14 @@ impl HolderSM { } } + pub fn get_attachment(&self) -> VcxResult { + match self.state { + HolderState::Finished(ref state) => state.get_attachment(), + HolderState::OfferReceived(ref state) => state.get_attachment(), + _ => Err(VcxError::from_msg(VcxErrorKind::NotReady, "Cannot get credential attachment: credential offer or credential must be receieved first")) + } + } + pub fn delete_credential(&self) -> VcxResult<()> { trace!("Holder::delete_credential"); diff --git a/libvcx/src/aries/handlers/issuance/holder/states/finished.rs b/libvcx/src/aries/handlers/issuance/holder/states/finished.rs index 91a15bb79c..01c08ed146 100644 --- a/libvcx/src/aries/handlers/issuance/holder/states/finished.rs +++ b/libvcx/src/aries/handlers/issuance/holder/states/finished.rs @@ -13,10 +13,9 @@ pub struct FinishedHolderState { impl FinishedHolderState { pub fn get_attributes(&self) -> VcxResult { - let credential = self.credential.as_ref().ok_or(VcxError::from_msg(VcxErrorKind::InvalidState, "No credential found"))?; - let content = credential.credentials_attach.content()?; - let cred_data: CredentialData = serde_json::from_str(&content) - .map_err(|err| VcxError::from_msg(VcxErrorKind::InvalidJson, format!("Cannot deserialize {:?}, into CredentialData, err: {:?}", content, err)))?; + let attach = self.get_attachment()?; + let cred_data: CredentialData = serde_json::from_str(&attach) + .map_err(|err| VcxError::from_msg(VcxErrorKind::InvalidJson, format!("Cannot deserialize {:?}, into CredentialData, err: {:?}", attach, err)))?; let mut new_map = serde_json::map::Map::new(); match cred_data.values.as_object() { @@ -30,7 +29,12 @@ impl FinishedHolderState { }; Ok(serde_json::Value::Object(new_map).to_string()) } - _ => Err(VcxError::from_msg(VcxErrorKind::InvalidJson, format!("Cannot convert {:?} into object", content))) + _ => Err(VcxError::from_msg(VcxErrorKind::InvalidJson, format!("Cannot convert {:?} into object", attach))) } } + + pub fn get_attachment(&self) -> VcxResult { + let credential = self.credential.as_ref().ok_or(VcxError::from_msg(VcxErrorKind::InvalidState, "No credential found"))?; + credential.credentials_attach.content() + } } diff --git a/libvcx/src/aries/handlers/issuance/holder/states/offer_received.rs b/libvcx/src/aries/handlers/issuance/holder/states/offer_received.rs index dbaa50a444..93a410d49b 100644 --- a/libvcx/src/aries/handlers/issuance/holder/states/offer_received.rs +++ b/libvcx/src/aries/handlers/issuance/holder/states/offer_received.rs @@ -48,4 +48,8 @@ impl OfferReceivedState { }); Ok(serde_json::Value::Object(new_map).to_string()) } + + pub fn get_attachment(&self) -> VcxResult { + self.offer.offers_attach.content() + } } diff --git a/libvcx/src/credential.rs b/libvcx/src/credential.rs index f2f5eb1e53..0cc1851777 100644 --- a/libvcx/src/credential.rs +++ b/libvcx/src/credential.rs @@ -123,6 +123,12 @@ pub fn get_attributes(handle: u32) -> VcxResult { }) } +pub fn get_attachment(handle: u32) -> VcxResult { + HANDLE_MAP.get(handle, |credential| { + credential.get_attachment() + }) +} + pub fn delete_credential(handle: u32) -> VcxResult { let source_id = get_source_id(handle).unwrap_or_default(); trace!("Credential::delete_credential >>> credential_handle: {}, source_id: {}", handle, source_id); diff --git a/wrappers/ios/vcx/ConnectMeVcx.m b/wrappers/ios/vcx/ConnectMeVcx.m index e7ed9ec57b..a7ab027c37 100644 --- a/wrappers/ios/vcx/ConnectMeVcx.m +++ b/wrappers/ios/vcx/ConnectMeVcx.m @@ -813,6 +813,23 @@ - (void)credentialGetAttributes:(VcxHandle)credentialHandle } } +- (void)credentialGetAttachment:(VcxHandle)credentialHandle + completion:(void (^)(NSError *error, NSString *attach))completion{ + vcx_error_t ret; + vcx_command_handle_t handle = [[VcxCallbacks sharedInstance] createCommandHandleFor:completion]; + + ret = vcx_credential_get_attachment(handle, credentialHandle, VcxWrapperCommonStringCallback); + + if( ret != 0 ) + { + [[VcxCallbacks sharedInstance] deleteCommandHandleFor: handle]; + + dispatch_async(dispatch_get_main_queue(), ^{ + completion([NSError errorFromVcxError: ret],nil); + }); + } +} + - (void)generateProof:(NSString *)proofRequestId requestedAttrs:(NSString *)requestedAttrs requestedPredicates:(NSString *)requestedPredicates diff --git a/wrappers/ios/vcx/include/libvcx.h b/wrappers/ios/vcx/include/libvcx.h index 832f165064..0a6ca6fa69 100644 --- a/wrappers/ios/vcx/include/libvcx.h +++ b/wrappers/ios/vcx/include/libvcx.h @@ -351,6 +351,8 @@ vcx_error_t vcx_credential_get_offers(vcx_command_handle_t command_handle, vcx_c /** Get attributes for specified credential */ vcx_error_t vcx_credential_get_attributes(vcx_command_handle_t handle, vcx_credential_handle_t credential_handle, void (*cb)(vcx_command_handle_t command_handle, vcx_error_t err, const char *attributes)); +vcx_error_t vcx_credential_get_attachment(vcx_command_handle_t handle, vcx_credential_handle_t credential_handle, void (*cb)(vcx_command_handle_t command_handle, vcx_error_t err, const char *attachment)); + /** Updates the state of the credential from the agency. */ vcx_error_t vcx_credential_update_state(vcx_command_handle_t command_handle, vcx_credential_handle_t credential_handle, void (*cb)(vcx_command_handle_t xcommand_handle, vcx_error_t err, vcx_state_t state)); diff --git a/wrappers/java/src/main/java/com/evernym/sdk/vcx/LibVcx.java b/wrappers/java/src/main/java/com/evernym/sdk/vcx/LibVcx.java index 140c34e9b7..a30ec2af8b 100644 --- a/wrappers/java/src/main/java/com/evernym/sdk/vcx/LibVcx.java +++ b/wrappers/java/src/main/java/com/evernym/sdk/vcx/LibVcx.java @@ -531,6 +531,8 @@ public interface API extends Library { /** Retrieves attributes present in the credential or credential offer, depending on credential state */ public int vcx_credential_get_attributes(int command_handle, int credential_handle, Callback cb); + public int vcx_credential_get_attachment(int command_handle, int credential_handle, Callback cb); + /** Populates status with the current State of this credential. */ public int vcx_credential_serialize(int command_handle, int credential_handle, Callback cb); diff --git a/wrappers/java/src/main/java/com/evernym/sdk/vcx/credential/CredentialApi.java b/wrappers/java/src/main/java/com/evernym/sdk/vcx/credential/CredentialApi.java index 8257e57185..c978794099 100644 --- a/wrappers/java/src/main/java/com/evernym/sdk/vcx/credential/CredentialApi.java +++ b/wrappers/java/src/main/java/com/evernym/sdk/vcx/credential/CredentialApi.java @@ -313,6 +313,30 @@ public static CompletableFuture credentialGetAttributes( return future; } + private static Callback vcxCredentialGetAttachmentCB = new Callback() { + @SuppressWarnings({"unused", "unchecked"}) + public void callback(int command_handle, int err, String attachment) { + logger.debug("vcxCredentialGetAttachmentCB() called with: command_handle = [" + command_handle + "], err = [" + err + "], attachment = [" + attachment + "]"); + CompletableFuture future = (CompletableFuture) removeFuture(command_handle); + if (!checkCallback(future, err)) return; + future.complete(attachment); + } + }; + + public static CompletableFuture credentialGetAttachment( + int credentialHandle + ) throws VcxException { + ParamGuard.notNull(credentialHandle, "credentialHandle"); + logger.debug("getAttachment() called with: credentialHandle = [" + credentialHandle + "]"); + CompletableFuture future = new CompletableFuture(); + int commandHandle = addFuture(future); + + int result = LibVcx.api.vcx_credential_get_attachment(commandHandle, credentialHandle, vcxCredentialGetAttachmentCB); + checkResult(result); + + return future; + } + public static int credentialRelease(int credentialHandle) throws VcxException { ParamGuard.notNull(credentialHandle, "credentialHandle"); logger.debug("credentialRelease() called with: credentialHandle = [" + credentialHandle + "]"); diff --git a/wrappers/node/src/api/credential.ts b/wrappers/node/src/api/credential.ts index 57c8b1f093..aa941d7632 100644 --- a/wrappers/node/src/api/credential.ts +++ b/wrappers/node/src/api/credential.ts @@ -382,6 +382,33 @@ export class Credential extends VCXBaseWithState { } } + public async getAttachment (connection: Connection): Promise { + try { + const attach = await createFFICallbackPromise( + (resolve, reject, cb) => { + const rc = rustAPI().vcx_credential_get_attachment(0, this.handle, cb) + if (rc) { + reject(rc) + } + }, + (resolve, reject) => Callback( + 'void', + ['uint32', 'uint32', 'string'], + (handle: number, err: number, messages: string) => { + if (err) { + reject(err) + return + } + resolve(messages) + }) + ) + return attach + } catch (err) { + throw new VCXInternalError(err) + } + } + + get credOffer (): string { return this._credOffer } diff --git a/wrappers/node/src/rustlib.ts b/wrappers/node/src/rustlib.ts index 6d6cb0b7ca..94fd5eef56 100644 --- a/wrappers/node/src/rustlib.ts +++ b/wrappers/node/src/rustlib.ts @@ -208,6 +208,7 @@ export interface IFFIEntryPoint { vcx_credential_get_state: (commandId: number, handle: number, cb: any) => number, vcx_credential_get_offers: (commandId: number, connectionHandle: number, cb: any) => number, vcx_credential_get_attributes: (commandId: number, connectionHandle: number, cb: any) => number, + vcx_credential_get_attachment: (commandId: number, connectionHandle: number, cb: any) => number, vcx_credential_get_payment_info: (commandId: number, handle: number, cb: any) => number, vcx_credential_get_payment_txn: (commandId: number, handle: number, cb: any) => number, @@ -428,6 +429,8 @@ export const FFIConfiguration: { [ Key in keyof IFFIEntryPoint ]: any } = { vcx_credential_get_offers: [FFI_ERROR_CODE, [FFI_COMMAND_HANDLE, FFI_CONNECTION_HANDLE, FFI_CALLBACK_PTR]], vcx_credential_get_attributes: [FFI_ERROR_CODE, [FFI_COMMAND_HANDLE, FFI_CONNECTION_HANDLE, FFI_CALLBACK_PTR]], + vcx_credential_get_attachment: [FFI_ERROR_CODE, [FFI_COMMAND_HANDLE, FFI_CONNECTION_HANDLE, + FFI_CALLBACK_PTR]], vcx_credential_get_payment_info: [FFI_ERROR_CODE, [FFI_COMMAND_HANDLE, FFI_CREDENTIAL_HANDLE,FFI_CALLBACK_PTR]], vcx_credential_get_payment_txn: [FFI_ERROR_CODE, [FFI_COMMAND_HANDLE, FFI_CREDENTIAL_HANDLE,FFI_CALLBACK_PTR]], diff --git a/wrappers/node/test/suite1/ariesvcx-credential.test.ts b/wrappers/node/test/suite1/ariesvcx-credential.test.ts index 70eda13923..98d314f77e 100644 --- a/wrappers/node/test/suite1/ariesvcx-credential.test.ts +++ b/wrappers/node/test/suite1/ariesvcx-credential.test.ts @@ -176,6 +176,23 @@ describe('Credential:', () => { }) }) + describe('getAttachment:', () => { + it('success', async () => { + const connection = await createConnectionInviterRequested() + const offers = await Credential.getOffers(connection) + assert.ok(offers) + assert.ok(offers.length) + const offer = offers[0] + const credential = await credentialCreateWithOffer({ + connection, + offer: JSON.stringify(offer), + sourceId: 'credentialGetAttributesTestSourceId' + }) + const attach = JSON.parse(await credential.getAttachment(connection)) + assert.deepEqual(attach.schema_id, 'V4SGRU86Z58d6TV7PBUe6f:2:FaberVcx:83.23.62') + }) + }) + describe('getPaymentInfo:', () => { it.skip('success', async () => { const credential = await credentialCreateWithOffer()