diff --git a/package.json b/package.json index 902c658..5ceedd6 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,3 @@ - { "name": "react-native-arnima-sdk", "version": "1.0.0", diff --git a/src/agent/index.ts b/src/agent/index.ts index 0cbfdd2..0e9ef7c 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -11,7 +11,7 @@ import { NativeModules } from 'react-native'; import { WalletConfig, WalletCredentials } from '../wallet/WalletInterface'; import BasicMessageService from '../protocols/basicMessage/BasicMessageService'; import ConnectionService from '../protocols/connection/ConnectionService'; -import ConnectWithMediatorService from '../protocols/mediator/ConnectWithMediatorService'; +import MediatorService from '../protocols/mediator/MediatorService'; import CredentialService from '../protocols/credential/CredentialService'; import DatabaseServices from '../storage'; import InboundMessageService from '../transports'; @@ -19,6 +19,7 @@ import PoolService from '../pool'; import PresentationService from '../protocols/presentation/PresentationService'; import WalletService from '../wallet/WalletService'; import WalletStorageService from '../wallet/WalletStorageService'; +import { Connection } from 'react-native-arnima-sdk/src/protocols/connection/ConnectionInterface'; const { ArnimaSdk } = NativeModules; @@ -40,8 +41,8 @@ class Agent { connectWithMediator = async (url: string, apiType: string, apiBody: string) => { try { - const response = await ConnectWithMediatorService.ConnectWithMediator(url, apiType, apiBody); this.wallet = await DatabaseServices.getWallet(); + const response = await MediatorService.ConnectWithMediator(url, apiType, apiBody); return response; } catch (error) { @@ -50,9 +51,49 @@ class Agent { } } + connectMediatorWithInvite = async (invitationUrl: string) => { + const connection = await this.acceptInvitation({},invitationUrl,''); + + setTimeout(() => { + ConnectionService.connectionStatus( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + connection.verkey, + ) + }, 5000); + connection.state == 'COMPLETE' + } + + mediationRequest = async (connection: Connection) => { + try { + await MediatorService.mediationRequest(connection); + } catch (error) { + console.log("Agent - mediationRequest error = ", error); + throw error; + } + } + + pickupMessages = async (mediatorConnection: Connection) => { + try { + await MediatorService.pickupMessages(mediatorConnection); + } catch (error) { + console.log("Agent - pickupMessages error = ", error); + throw error; + } + } + + sendImplicitMessages = async (mediatorConnection: Connection) => { + try { + await MediatorService.sendImplicitMessages(mediatorConnection); + } catch (error) { + console.log("Agent - pickupMessages error = ", error); + throw error; + } + } + updateMediator = async (url: string, apiType: string, apiBody: string) => { try { - return await ConnectWithMediatorService.updateMediator(url, apiType, apiBody); + return await MediatorService.updateMediator(url, apiType, apiBody); } catch (error) { console.log("Agent - Update mediator error = ", error); @@ -115,10 +156,17 @@ class Agent { } } - acceptInvitation = async (didJson: Object, message: any, logo: string,) => { + acceptInvitation = async (didJson: Object, message: any, logo: string) => { try { + this.wallet = await DatabaseServices.getWallet(); const invitation = decodeInvitationFromUrl(message); - return await ConnectionService.acceptInvitation(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), didJson, invitation, logo); + return await ConnectionService.acceptInvitation( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + didJson, + invitation, + logo, + ); } catch (error) { console.log("Agent - Accept invitation error = ", error); @@ -136,6 +184,16 @@ class Agent { } } + getMediatorRecord = async (query: Object) => { + try { + return await WalletStorageService.getWalletRecordsFromQuery(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.MediatorAgent, JSON.stringify(query)); + } + catch (error) { + console.log("Agent - Get all connections error = ", error); + throw error; + } + } + getPresentationRecord = async (query: Object) => { try { return await WalletStorageService.getWalletRecordsFromQuery(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.Presentation, JSON.stringify(query)); diff --git a/src/network/index.ts b/src/network/index.ts index 0bbcb2d..e3fface 100644 --- a/src/network/index.ts +++ b/src/network/index.ts @@ -2,6 +2,7 @@ Copyright AyanWorks Technology Solutions Pvt. Ltd. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ +import InboundMessageService from '../transports'; export const NetworkServices: Function = async (url: string, apiType: string, apiBody: string) => { try { @@ -26,28 +27,36 @@ export const NetworkServices: Function = async (url: string, apiType: string, ap export const OutboundAgentMessage: Function = async (url: string, apiType: string, apiBody: string) => { try { - return new Promise(async function ( - resolve, reject - ) { - const response = await fetch(url, { - method: apiType, - body: apiBody, - headers: { - Accept: 'application/json', - 'Content-Type': 'application/ssi-agent-wire', - }, - }).then((response) => { - response.json() - }) - .then((json) => { - resolve(json); - }) - .catch((error) => { - reject('We are not able to communicate with the agent at this moment, Please try again later'); - }); + console.log("url", url) + const abortController = new AbortController() + const id = setTimeout(() => abortController.abort(), 4000) + const response = await fetch(url, { + method: 'POST', + body: apiBody, + headers: { 'Content-Type': 'application/ssi-agent-wire' }, + signal: abortController.signal, + }) + clearTimeout(id) + const responseMessage = await response.text() + if (responseMessage) { + console.log(`Response received`) + try { + const wireMessage = JSON.parse(responseMessage) + if (wireMessage.hasOwnProperty('tag')) { + await InboundMessageService.addMessages(wireMessage) + } + } catch (error) { + console.log('Unable to parse response message', error) + } + } else { + console.log(`No response received.`) } - ) } catch (error) { - throw new Error('We are not able to communicate with the agent at this moment, Please try again later'); + console.log('Error OutboundAgentMessage', error) + if (error.name == 'AbortError') { + console.log('Signal aborted') + } else { + throw new Error('We are not able to communicate with the agent at this moment, Please try again later'); + } } }; \ No newline at end of file diff --git a/src/protocols/connection/ConnectionService.ts b/src/protocols/connection/ConnectionService.ts index b29afe8..eef5d10 100644 --- a/src/protocols/connection/ConnectionService.ts +++ b/src/protocols/connection/ConnectionService.ts @@ -17,11 +17,11 @@ import { Connection } from './ConnectionInterface'; import { ConnectionState } from './ConnectionState'; import DatabaseServices from '../../storage'; import { NativeModules } from "react-native"; -import { NetworkServices } from "../../network"; import { TrustPing } from "../trustPing/TrustPingInterface"; import { TrustPingState } from '../trustPing/TrustPingState'; import WalletStorageService from '../../wallet/WalletStorageService'; import { createTrustPingMessage } from '../trustPing/TrustPingMessages'; +import MediatorService from '../mediator/MediatorService'; import { EventInterface } from 'react-native-arnima-sdk/src/agent/EventInterface'; import { EventRegister } from 'react-native-event-listeners'; @@ -65,7 +65,9 @@ class ConnectionService { credentialsJson: WalletCredentials, didJson: Object, logo: string): Promise { - const connection: Connection = await this.createConnection(configJson, credentialsJson, didJson); + + const routing = await MediatorService.getRouting(); + const connection: Connection = await this.createConnection(routing, ''); const connectionTags: Object = { connectionId: connection.verkey, @@ -98,20 +100,24 @@ class ConnectionService { * @return {*} {Promise} * @memberof ConnectionService */ - async acceptInvitation(configJson: WalletConfig, + async acceptInvitation( + configJson: WalletConfig, credentialsJson: WalletCredentials, didJson: Object, invitation: Message, - logo: string): Promise { + logo: string, + ): Promise { try { - const connection: Connection = await this.createConnection(configJson, credentialsJson, didJson, invitation.label, + const routing = await MediatorService.getRouting(); + const connection: Connection = await this.createConnection( + routing, + invitation.label, invitation.hasOwnProperty('alias') ? invitation.alias.logoUrl : '', - invitation.hasOwnProperty('alias') ? invitation.alias.organizationId : ''); + invitation.hasOwnProperty('alias') ? invitation.alias.organizationId : '', + ); const connectionRequest = createConnectionRequestMessage(connection, DatabaseServices.getLabel(), logo); connection.state = ConnectionState.REQUESTED; - await sendOutboundMessage(configJson, credentialsJson, connection, connectionRequest, invitation) - const connectionTags: Object = { connectionId: connection.verkey, } @@ -124,9 +130,10 @@ class ConnectionService { JSON.stringify(connection), JSON.stringify(connectionTags) ); + await sendOutboundMessage(configJson, credentialsJson, connection, connectionRequest, invitation) return connection; } catch (error) { - console.log('Connection - Create invitation error = ', JSON.stringify(error)); + console.log('Connection - Accept invitation error = ', JSON.stringify(error)); throw error; } } @@ -282,23 +289,22 @@ class ConnectionService { * @return {*} {Promise} * @memberof ConnectionService */ - async createConnection(configJson: WalletConfig, - credentialsJson: WalletCredentials, - didJson: Object, + async createConnection( + routing: { + endpoint: string; + routingKeys: string[]; + pairwiseDid: string; + verkey: string; + }, label?: string, logo?: string, organizationId?: string, ): Promise { try { - const [pairwiseDid, verkey]: string[] = await ArnimaSdk.createAndStoreMyDid(JSON.stringify(configJson), JSON.stringify(credentialsJson), JSON.stringify(didJson), false); + const { endpoint, pairwiseDid, routingKeys, verkey } = routing; - const apiBody = { - publicVerkey: DatabaseServices.getVerKey(), - verkey: verkey - }; - await NetworkServices(getServiceEndpoint() + 'verkey', 'POST', JSON.stringify(apiBody)); const publicKey = new PublicKey(`${pairwiseDid}#1`, PublicKeyType.ED25519_SIG_2018, pairwiseDid, verkey); - const service = new Service(`${pairwiseDid};indy`, DatabaseServices.getServiceEndpoint(), [verkey], [DatabaseServices.getRoutingKeys()], 0, 'IndyAgent'); + const service = new Service(`${pairwiseDid};indy`, endpoint, [verkey], routingKeys, 0, 'IndyAgent'); const auth = new Authentication(publicKey); const did_doc = new DidDoc(pairwiseDid, [auth], [publicKey], [service]); @@ -316,7 +322,6 @@ class ConnectionService { createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; - return connection; } catch (error) { @@ -324,6 +329,29 @@ class ConnectionService { throw error; } } + + + async connectionStatus( + configJson: WalletConfig, + credentialsJson: WalletCredentials, + verkey:string, + ) { + try { + const query = { + connectionId: verkey + } + const connection: Connection = await WalletStorageService.getWalletRecordFromQuery(configJson, credentialsJson, RecordType.Connection, JSON.stringify(query)); + if (!connection) { + throw new Error(`Connection for verkey ${verkey} not found!`); + } + return connection; + } + catch (error) { + console.log('Connection - Connection status error = ', JSON.stringify(error)); + throw error; + } + } + } export default new ConnectionService(); \ No newline at end of file diff --git a/src/protocols/mediator/ConnectWithMediatorService.ts b/src/protocols/mediator/ConnectWithMediatorService.ts deleted file mode 100644 index 8df37c6..0000000 --- a/src/protocols/mediator/ConnectWithMediatorService.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - Copyright AyanWorks Technology Solutions Pvt. Ltd. All Rights Reserved. - SPDX-License-Identifier: Apache-2.0 -*/ - -import { MediatorResponse } from "./MediatorInterface"; -import { NetworkServices } from "../../network"; -import { RecordType } from '../../utils/Helpers' -import { WalletRecord } from '../../wallet/WalletInterface'; -import DatabaseServices from "../../storage"; -import WalletStorageService from '../../wallet/WalletStorageService'; - -class MediatorService { - - /** - * Connect the mediator agent and store the mediator record - * - * @param {string} url - * @param {string} apiType - * @param {string} apiBody - * @return {*} {Promise} - * @memberof MediatorService - */ - async ConnectWithMediator(url: string, apiType: string, apiBody: string): Promise { - try { - - const { data: { routingKeys, serviceEndpoint } }: MediatorResponse = await NetworkServices(url, apiType, apiBody); - // TODO : Need to find a way for realm db typing - const myWallet: any = DatabaseServices.getWallet(); - - DatabaseServices.storeWallet({ - walletConfig: myWallet.walletConfig, - walletCredentials: myWallet.credentialsJson, - label: myWallet.label, - serviceEndpoint: serviceEndpoint, - routingKey: routingKeys[0], - publicDid: myWallet.publicDid, - verKey: myWallet.verKey, - masterSecretId: myWallet.masterSecretId, - }); - - const walletRecord: WalletRecord = { - walletConfig: myWallet.walletConfig, - walletCredentials: myWallet.credentialsJson, - label: myWallet.label, - serviceEndpoint: serviceEndpoint, - routingKey: routingKeys[0], - publicDid: myWallet.publicDid, - verKey: myWallet.verKey, - masterSecretId: myWallet.masterSecretId, - } - - await WalletStorageService.updateWalletRecord( - myWallet.walletConfig, - myWallet.walletConfig, - RecordType.MediatorAgent, - '1', - JSON.stringify(walletRecord), - '{}' - ); - - return Promise.resolve(true); - } - catch (error) { - console.log('MediatorService - Connect with mediator error = ', error); - throw error; - } - } - - /** - * Update public did and verkey on mediator - * - * @param {string} url - * @param {string} apiType - * @param {string} apiBody - * @return {*} {Promise} - * @memberof MediatorService - */ - async updateMediator(url: string, apiType: string, apiBody: string): Promise { - try { - await NetworkServices(url, apiType, apiBody); - return Promise.resolve(true); - } - catch (error) { - console.log('MediatorService - Update mediator error = ', error); - return Promise.reject(error); - } - } - -} - -export default new MediatorService(); diff --git a/src/protocols/mediator/MediationMessages.ts b/src/protocols/mediator/MediationMessages.ts new file mode 100644 index 0000000..2aa27cb --- /dev/null +++ b/src/protocols/mediator/MediationMessages.ts @@ -0,0 +1,37 @@ +/* + Copyright AyanWorks Technology Solutions Pvt. Ltd. All Rights Reserved. + SPDX-License-Identifier: Apache-2.0 +*/ + +import { MessageType } from "../../utils/MessageType"; +import uuid from 'uuid/v4'; + +export async function createMediationRequestMessage() { + return { + '@type': MessageType.MediationRequest, + '@id': uuid(), + sent_time: new Date().toISOString(), + '~l10n': { + locale: 'en', + } + }; +} + +export async function createBatchPickupMessage(batchSize: number = 10) { + return { + '@type': MessageType.BatchPickup, + '@id': uuid(), + batch_size: batchSize, + }; +} + +export async function createKeylistUpdateMessage(verkey: string) { + return { + '@type': MessageType.KeyListUpdate, + '@id': uuid(), + updates: [{ + "recipient_key": verkey, + action: "add" + }] + }; +} \ No newline at end of file diff --git a/src/protocols/mediator/MediatorService.ts b/src/protocols/mediator/MediatorService.ts new file mode 100644 index 0000000..050e671 --- /dev/null +++ b/src/protocols/mediator/MediatorService.ts @@ -0,0 +1,295 @@ +/* + Copyright AyanWorks Technology Solutions Pvt. Ltd. All Rights Reserved. + SPDX-License-Identifier: Apache-2.0 +*/ +import { MediatorResponse } from "react-native-arnima-sdk/src/protocols/mediator/MediatorInterface"; +import { NetworkServices } from "react-native-arnima-sdk/src/network"; +import { RecordType, sendOutboundMessage } from 'react-native-arnima-sdk/src/utils/Helpers' +import { WalletRecord } from 'react-native-arnima-sdk/src/wallet/WalletInterface'; +import DatabaseServices from "react-native-arnima-sdk/src/storage"; +import WalletStorageService from 'react-native-arnima-sdk/src/wallet/WalletStorageService'; +import { createBatchPickupMessage, createKeylistUpdateMessage, createMediationRequestMessage } from "react-native-arnima-sdk/src/protocols/mediator/MediationMessages"; +import { Connection } from "react-native-arnima-sdk/src/protocols/connection/ConnectionInterface"; +import { NativeModules } from "react-native"; +import { createTrustPingMessage } from "react-native-arnima-sdk/src/protocols/trustPing/TrustPingMessages"; +import InboundService from '../../transports' +const { ArnimaSdk } = NativeModules; + +class MediatorService { + + private transportSessions: Map = new Map() + private webSocket: typeof WebSocket + + constructor() { + this.webSocket = WebSocket; + } + + async sendWebSocketMessage(endpoint: string, outboundPackMessage: string) { + try { + + const socket = await this.resolveSocket(endpoint) + + socket.send(Buffer.from(JSON.stringify(outboundPackMessage))) + + } catch (error) { + console.log('MediatorService - sendWebSocketMessage error = ', error); + throw error; + } + } + + private async resolveSocket(endpoint: string) { + // If we already have a socket connection use it + let socket = this.transportSessions.get(endpoint) + + if (!socket) { + if (!endpoint) { + throw new Error(`Missing endpoint. I don't know how and where to send the message.`) + } + socket = await this.createSocketConnection(endpoint) + this.transportSessions.set(endpoint, socket) + this.listenOnWebSocketMessages(socket) + } + + if (socket.readyState !== this.webSocket.OPEN) { + throw new Error('Socket is not open.') + } + + return socket + } + + private handleMessageEvent = async (event: any) => { + console.log('WebSocket message event received.', { url: event.target.url, data: event.data }) + const wireMessage = JSON.parse(Buffer.from(event.data).toString('utf-8')) + if (wireMessage) { + console.log(`Response received`, wireMessage) + try { + if (wireMessage.hasOwnProperty('tag')) { + await InboundService.addMessages(wireMessage) + } + } catch (error) { + console.log('Unable to parse response message', error) + } + } + } + + private listenOnWebSocketMessages(socket: WebSocket) { + socket.addEventListener('message', this.handleMessageEvent) + } + + private createSocketConnection(endpoint: string): Promise { + return new Promise((resolve, reject) => { + console.log(`Connecting to WebSocket ${endpoint}`) + const socket = new this.webSocket(endpoint) + + socket.onopen = () => { + console.log(`Successfully connected to WebSocket ${endpoint}`) + resolve(socket) + } + + socket.onerror = (error) => { + console.log(`Error while connecting to WebSocket ${endpoint}`, { + error, + }) + reject(error) + } + + socket.onclose = async (event) => { + console.log(`WebSocket closing of endpoint ${endpoint}`, { event }) + socket.removeEventListener('message', this.handleMessageEvent) + this.transportSessions.delete(endpoint) + } + }) + } + + async sendImplicitMessages(mediatorConnection: Connection) { + try { + const myWallet: any = DatabaseServices.getWallet(); + + const trustPingMessage = await createTrustPingMessage(false); + + await sendOutboundMessage( + JSON.parse(myWallet.walletConfig), + JSON.parse(myWallet.walletConfig), + mediatorConnection, + trustPingMessage + ); + } catch (err) { + console.log('sendImplicitMessage Error: ', err); + throw err; + } + } + + async mediationRequest(connection: Connection) { + const myWallet: any = DatabaseServices.getWallet(); + const message = await createMediationRequestMessage(); + + const connectionTags: Object = { + connectionId: connection.verkey, + } + await WalletStorageService.addWalletRecord( + myWallet.walletConfig, + myWallet.walletConfig, + RecordType.MediatorAgent, + connection.verkey, + JSON.stringify(connection), + JSON.stringify(connectionTags) + ); + await sendOutboundMessage( + JSON.parse(myWallet.walletConfig), + JSON.parse(myWallet.walletConfig), + connection, + message + ); + return true + } + + async saveRoutingKeys(message) { + const myWallet: any = DatabaseServices.getWallet(); + + DatabaseServices.storeWallet({ + walletConfig: myWallet.walletConfig, + walletCredentials: myWallet.credentialsJson, + label: myWallet.label, + serviceEndpoint: message.endpoint, + routingKey: message.routing_keys[0], + publicDid: myWallet.publicDid, + verKey: myWallet.verKey, + masterSecretId: myWallet.masterSecretId, + }); + return true + } + + async pickupMessages(mediatorConnection: Connection) { + const myWallet: any = DatabaseServices.getWallet(); + const batchPickupMessage = await createBatchPickupMessage(); + await sendOutboundMessage( + JSON.parse(myWallet.walletConfig), + JSON.parse(myWallet.walletCredentials), + mediatorConnection, + batchPickupMessage, + ); + return true + } + + async getRouting() { + try { + const myWallet: any = DatabaseServices.getWallet(); + const endpoint = myWallet.serviceEndpoint !== "" ? myWallet.serviceEndpoint : '' + + const routingKeys: string[] = myWallet.routingKey !== '' ? [myWallet.routingKey] : [] + const [pairwiseDid, verkey]: string[] = await ArnimaSdk.createAndStoreMyDid( + myWallet.walletConfig, + myWallet.walletCredentials, + JSON.stringify({}), + false + ); + await this.keylistUpdate(verkey); + return { endpoint, routingKeys, pairwiseDid, verkey }; + } catch (error) { + console.log('MediatorService - getRouting error = ', error); + throw error; + } + } + + async keylistUpdate(verkey: string) { + try { + const myWallet: any = DatabaseServices.getWallet(); + const keylistUpdateMessage = await createKeylistUpdateMessage(verkey); + const [mediatorConnection] = await WalletStorageService.getWalletRecordsFromQuery( + JSON.parse(myWallet.walletConfig), + JSON.parse(myWallet.walletCredentials), + RecordType.MediatorAgent, + JSON.stringify({}) + ); + if (mediatorConnection) { + await sendOutboundMessage( + JSON.parse(myWallet.walletConfig), + JSON.parse(myWallet.walletCredentials), + JSON.parse(mediatorConnection.value), + keylistUpdateMessage, + ) + } + } catch (error) { + console.log('MediatorService - keylistUpdate error = ', error); + throw error; + } + } + + /** + * Connect the mediator agent and store the mediator record + * + * @param {string} url + * @param {string} apiType + * @param {string} apiBody + * @return {*} {Promise} + * @memberof MediatorService + */ + async ConnectWithMediator(url: string, apiType: string, apiBody: string): Promise { + try { + + const { data: { routingKeys, serviceEndpoint } }: MediatorResponse = await NetworkServices(url, apiType, apiBody); + // TODO : Need to find a way for realm db typing + const myWallet: any = DatabaseServices.getWallet(); + + DatabaseServices.storeWallet({ + walletConfig: myWallet.walletConfig, + walletCredentials: myWallet.credentialsJson, + label: myWallet.label, + serviceEndpoint: serviceEndpoint, + routingKey: routingKeys[0], + publicDid: myWallet.publicDid, + verKey: myWallet.verKey, + masterSecretId: myWallet.masterSecretId, + }); + + const walletRecord: WalletRecord = { + walletConfig: myWallet.walletConfig, + walletCredentials: myWallet.credentialsJson, + label: myWallet.label, + serviceEndpoint: serviceEndpoint, + routingKey: routingKeys[0], + publicDid: myWallet.publicDid, + verKey: myWallet.verKey, + masterSecretId: myWallet.masterSecretId, + } + + await WalletStorageService.updateWalletRecord( + myWallet.walletConfig, + myWallet.walletConfig, + RecordType.MediatorAgent, + '1', + JSON.stringify(walletRecord), + '{}' + ); + + return Promise.resolve(true); + } + catch (error) { + console.log('MediatorService - Connect with mediator error = ', error); + throw error; + } + } + + /** + * Update public did and verkey on mediator + * + * @param {string} url + * @param {string} apiType + * @param {string} apiBody + * @return {*} {Promise} + * @memberof MediatorService + */ + async updateMediator(url: string, apiType: string, apiBody: string): Promise { + try { + await NetworkServices(url, apiType, apiBody); + return Promise.resolve(true); + } + catch (error) { + console.log('MediatorService - Update mediator error = ', error); + return Promise.reject(error); + } + } + +} + +export default new MediatorService(); diff --git a/src/protocols/trustPing/TrustPingMessages.ts b/src/protocols/trustPing/TrustPingMessages.ts index 838ac79..75141b2 100644 --- a/src/protocols/trustPing/TrustPingMessages.ts +++ b/src/protocols/trustPing/TrustPingMessages.ts @@ -11,7 +11,7 @@ export function createTrustPingMessage(responseRequested: boolean = true, commen '@id': uuidv4(), '@type': MessageType.TrustPingMessage, ...(comment && { comment }), - responseRequested, + response_requested: responseRequested, }; } diff --git a/src/transports/index.ts b/src/transports/index.ts index 1ab8608..d0d761f 100644 --- a/src/transports/index.ts +++ b/src/transports/index.ts @@ -3,37 +3,46 @@ SPDX-License-Identifier: Apache-2.0 */ -import { Connection } from "../protocols/connection/ConnectionInterface"; -import { EventInterface } from "../agent/EventInterface"; -import { EventRegister } from 'react-native-event-listeners'; -import { MessageType } from "../utils/MessageType"; -import { Record } from "../wallet/WalletInterface"; -import { RecordType, getServiceEndpoint, unpackMessage } from "../utils/Helpers"; -import BasicMessageService from "../protocols/basicMessage/BasicMessageService"; -import ConnectionService from "../protocols/connection/ConnectionService"; -import CredentialService from "../protocols/credential/CredentialService"; -import DatabaseServices from "../storage"; -import io from "socket.io-client"; -import PresentationService from "../protocols/presentation/PresentationService"; -import TrustPingService from "../protocols/trustPing/TrustPingService"; -import WalletStorageService from "../wallet/WalletStorageService"; +import {Connection} from '../protocols/connection/ConnectionInterface'; +import {EventInterface} from '../agent/EventInterface'; +import {EventRegister} from 'react-native-event-listeners'; +import {MessageType} from '../utils/MessageType'; +import {Record} from '../wallet/WalletInterface'; +import { + RecordType, + getServiceEndpoint, + unpackMessage, + replaceDidSovPrefixOnMessage, +} from '../utils/Helpers'; +import BasicMessageService from '../protocols/basicMessage/BasicMessageService'; +import ConnectionService from '../protocols/connection/ConnectionService'; +import CredentialService from '../protocols/credential/CredentialService'; +import DatabaseServices from '../storage'; +import io from 'socket.io-client'; +import PresentationService from '../protocols/presentation/PresentationService'; +import TrustPingService from '../protocols/trustPing/TrustPingService'; +import WalletStorageService from '../wallet/WalletStorageService'; +import MediatorService from '../protocols/mediator/MediatorService'; class InboundMessageHandler { - isProcess: boolean = false; - socket: any + socket: any; wallet: any = DatabaseServices.getWallet(); initializeSocket = async () => { // TODO :Refactor the get wallet condition - this.wallet = await DatabaseServices.getWallet() - if (this.socket === undefined || (this.socket !== undefined && this.socket.disconnected)) { + this.wallet = await DatabaseServices.getWallet(); + if ( + this.socket === undefined || + (this.socket !== undefined && this.socket.disconnected) + ) { this.socket = io(getServiceEndpoint(), { - reconnection: true, reconnectionDelay: 500, + reconnection: true, + reconnectionDelay: 500, jsonp: false, reconnectionAttempts: Infinity, autoConnect: true, - transports: ['websocket'] + transports: ['websocket'], }); this.socketMessageListener(); this.socketEmitMessage(); @@ -41,7 +50,7 @@ class InboundMessageHandler { this.socketEmitMessage(); } this.socket.on('disconnect', (reason) => { - console.log("reason-----", reason); + console.log('reason-----', reason); if (reason === 'io server disconnect') { } this.socket.connect(); @@ -49,30 +58,42 @@ class InboundMessageHandler { }; socketEmitMessage = () => { - this.socket.emit("message", DatabaseServices.getVerKey()); + this.socket.emit('message', DatabaseServices.getVerKey()); }; socketMessageListener = async () => { - this.socket.on("message", async (msg) => { + this.socket.on('message', async (msg) => { let inboxId: string = ''; if (msg.length > 0) { for await (let message of msg) { - inboxId = inboxId + message.id + "," + inboxId = inboxId + message.id + ','; const ssiMessageTags = { messageId: message.id + '', autoProcessed: JSON.stringify(true), isProcessed: JSON.stringify(false), - message: typeof message.message == 'string' ? message.message : JSON.stringify(message.message) - } - const walletRecords = await WalletStorageService.getWalletRecordsFromQuery(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, JSON.stringify({ 'messageId': message.id + '' })); + message: + typeof message.message == 'string' + ? message.message + : JSON.stringify(message.message), + }; + const walletRecords = + await WalletStorageService.getWalletRecordsFromQuery( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + JSON.stringify({messageId: message.id + ''}), + ); if (walletRecords.length === 0) { await WalletStorageService.addWalletRecord( - JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, message.id + '', - typeof message.message == 'string' ? message.message : JSON.stringify(message.message), - JSON.stringify(ssiMessageTags) + typeof message.message == 'string' + ? message.message + : JSON.stringify(message.message), + JSON.stringify(ssiMessageTags), ); } } @@ -87,188 +108,390 @@ class InboundMessageHandler { inboxId = inboxId.substring(0, inboxId.length - 1); const apiBody = { publicKey: DatabaseServices.getVerKey(), - inboxId: inboxId + inboxId: inboxId, }; this.socket.emit('receiveAcknowledgement', apiBody); - EventRegister.emit('inboundMessageStatusListener', `inboundMessageStatusListener`); + EventRegister.emit( + 'inboundMessageStatusListener', + `inboundMessageStatusListener`, + ); } } - inboundMessageStatusListener = EventRegister.addEventListener('inboundMessageStatusListener', async () => { - const query: Object = { isProcessed: JSON.stringify(false) } - const unprocessedMessages: Array = await WalletStorageService.getWalletRecordsFromQuery(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, JSON.stringify(query)); - if (this.isProcess === false && unprocessedMessages.length > 0) { - await this.proceedInboundMessage(); - } - }); + inboundMessageStatusListener = EventRegister.addEventListener( + 'inboundMessageStatusListener', + async () => { + const query: Object = {isProcessed: JSON.stringify(false)}; + const unprocessedMessages: Array = + await WalletStorageService.getWalletRecordsFromQuery( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + JSON.stringify(query), + ); + if (this.isProcess === false && unprocessedMessages.length > 0) { + await this.proceedInboundMessage(); + } + }, + ); - proceedInboundMessage = async () => { + addMessages = async (message: Object) => { + this.wallet = await DatabaseServices.getWallet(); + const ssiMessageTags = { + messageId: Math.floor(Math.random() * 1000000000).toString(), + autoProcessed: JSON.stringify(true), + isProcessed: JSON.stringify(false), + }; + await WalletStorageService.addWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + ssiMessageTags.messageId, + JSON.stringify(message), + JSON.stringify(ssiMessageTags), + ); + EventRegister.emit( + 'inboundMessageStatusListener', + `inboundMessageStatusListener`, + ); + }; + + proceedInboundMessage = async () => { try { - const query: Object = { isProcessed: JSON.stringify(false) } - let unprocessedMessages: Array = await WalletStorageService.getWalletRecordsFromQuery(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, JSON.stringify(query)); + const query: Object = {isProcessed: JSON.stringify(false)}; + let unprocessedMessages: Array = + await WalletStorageService.getWalletRecordsFromQuery( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + JSON.stringify(query), + ); if (unprocessedMessages === null) { - unprocessedMessages = [] + unprocessedMessages = []; } for (let i = 0; i < unprocessedMessages.length; i++) { this.isProcess = true; if (unprocessedMessages[i].tags.autoProcessed === 'true') { const messageRecord = JSON.parse(unprocessedMessages[i].value); - const unpackMessageResponse = await unpackMessage(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), messageRecord.msg); + const unpackMessageResponse = await unpackMessage( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + messageRecord, + ); const message = JSON.parse(unpackMessageResponse.message); + replaceDidSovPrefixOnMessage(message); + console.log('Message Type = ', message['@type']); + console.log( + 'unpackMessageResponse', + JSON.stringify(message, null, 2), + ); const query = { - connectionId: unpackMessageResponse.recipient_verkey - } + connectionId: unpackMessageResponse.recipient_verkey, + }; - const connection = await WalletStorageService.getWalletRecordFromQuery(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.Connection, JSON.stringify(query)); - console.log('connection', typeof connection) + const connection = + await WalletStorageService.getWalletRecordFromQuery( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.Connection, + JSON.stringify(query), + ); if (connection.length === 0 || connection.verkey === '') { - console.log('Connection not found') - await WalletStorageService.deleteWalletRecord(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id); + console.log('Connection not found'); + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); this.isProcess = false; return; } switch (message['@type']) { case MessageType.ConnectionResponse: { - const isCompleted = await ConnectionService.processRequest(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), unpackMessageResponse); - if (isCompleted === true) await WalletStorageService.deleteWalletRecord(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id); + const isCompleted = await ConnectionService.processRequest( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + unpackMessageResponse, + ); + if (isCompleted === true) + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); break; } case MessageType.ConnectionRequest: { - const isCompleted = await ConnectionService.createResponse(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), unpackMessageResponse); - if (isCompleted === true) await WalletStorageService.deleteWalletRecord(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id); + const isCompleted = await ConnectionService.createResponse( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + unpackMessageResponse, + ); + if (isCompleted === true) + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); break; } case MessageType.TrustPingMessage: { - const connection: Connection = await TrustPingService.processPing(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), unpackMessageResponse); - if (connection !== null) { await WalletStorageService.deleteWalletRecord(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id) } + const connection: Connection = await TrustPingService.processPing( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + unpackMessageResponse, + ); + if (connection !== null) { + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); + } const event: EventInterface = { message: `You are now connected with ${connection.theirLabel}`, type: 'Connection', - } + }; EventRegister.emit('SDKEvent', event); - console.log("Connected by scanning the QR code ..."); + console.log('Connected by scanning the QR code ...'); break; } case MessageType.TrustPingResponseMessage: { - const connection: Connection = await TrustPingService.saveTrustPingResponse(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), unpackMessageResponse); - if (connection !== null) { await WalletStorageService.deleteWalletRecord(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id) } + const connection: Connection = + await TrustPingService.saveTrustPingResponse( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + unpackMessageResponse, + ); + if (connection !== null) { + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); + } break; } case MessageType.OfferCredential: { - let { message } = unpackMessageResponse; + let {message} = unpackMessageResponse; message = JSON.parse(message); unpackMessageResponse['message'] = message; const ssiMessageTags = { messageId: unprocessedMessages[i].id + '', autoProcessed: JSON.stringify(false), - thId: message.hasOwnProperty('~thread') ? Object.keys(message['~thread']).length > 0 === false ? message['@id'] : message['~thread'].thid : message['@id'], + thId: message.hasOwnProperty('~thread') + ? Object.keys(message['~thread']).length > 0 === false + ? message['@id'] + : message['~thread'].thid + : message['@id'], isProcessed: JSON.stringify(true), - } + }; await WalletStorageService.updateWalletRecord( - JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id + '', JSON.stringify(unpackMessageResponse), - JSON.stringify(ssiMessageTags) + JSON.stringify(ssiMessageTags), + ); + const connection = await CredentialService.requestReceived( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + unpackMessageResponse, + unprocessedMessages[i].id, ); - const connection = await CredentialService.requestReceived(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), unpackMessageResponse, unprocessedMessages[i].id); const event: EventInterface = { message: `You have received a credential from ${connection.theirLabel}`, type: 'Credential', - messageData: JSON.stringify({ connection }) - } + messageData: JSON.stringify({connection}), + }; EventRegister.emit('SDKEvent', event); break; } case MessageType.IssueCredential: { - const isCompleted = await CredentialService.processCredential(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), unpackMessageResponse); - if (isCompleted) { await WalletStorageService.deleteWalletRecord(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id) } + const isCompleted = await CredentialService.processCredential( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + unpackMessageResponse, + ); + if (isCompleted) { + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); + } break; } case MessageType.RequestPresentation: { - let { message } = unpackMessageResponse; + let {message} = unpackMessageResponse; message = JSON.parse(message); unpackMessageResponse['message'] = message; const ssiMessageTags = { messageId: unprocessedMessages[i].id + '', autoProcessed: JSON.stringify(false), - thId: message.hasOwnProperty('~thread') ? Object.keys(message['~thread']).length > 0 === false ? message['@id'] : message['~thread'].thid : message['@id'], + thId: message.hasOwnProperty('~thread') + ? Object.keys(message['~thread']).length > 0 === false + ? message['@id'] + : message['~thread'].thid + : message['@id'], isProcessed: JSON.stringify(true), - } + }; await WalletStorageService.updateWalletRecord( - JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id + '', JSON.stringify(unpackMessageResponse), - JSON.stringify(ssiMessageTags) + JSON.stringify(ssiMessageTags), + ); + const connection = await PresentationService.processRequest( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + unprocessedMessages[i].id, + unpackMessageResponse, ); - const connection = await PresentationService.processRequest(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), unprocessedMessages[i].id, unpackMessageResponse); const event: EventInterface = { message: `You have received a proof request from ${connection.theirLabel}`, type: 'Proof Request', - messageData: JSON.stringify({ connection }) - } + messageData: JSON.stringify({connection}), + }; EventRegister.emit('SDKEvent', event); break; } case MessageType.PresentationAck: { - await WalletStorageService.deleteWalletRecord(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id) + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); const event: EventInterface = { message: 'Your proof is verified successfully', type: 'Proof Status', - messageData: JSON.stringify({ connection }) - } + messageData: JSON.stringify({connection}), + }; EventRegister.emit('SDKEvent', event); break; } case MessageType.Presentation: { - let { message } = unpackMessageResponse; + let {message} = unpackMessageResponse; message = JSON.parse(message); unpackMessageResponse['message'] = message; const ssiMessageTags = { messageId: unprocessedMessages[i].id + '', autoProcessed: JSON.stringify(false), - thId: message.hasOwnProperty('~thread') ? Object.keys(message['~thread']).length > 0 === false ? message['@id'] : message['~thread'].thid : message['@id'], + thId: message.hasOwnProperty('~thread') + ? Object.keys(message['~thread']).length > 0 === false + ? message['@id'] + : message['~thread'].thid + : message['@id'], isProcessed: JSON.stringify(true), - } + }; await WalletStorageService.updateWalletRecord( - JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id + '', JSON.stringify(unpackMessageResponse), - JSON.stringify(ssiMessageTags) + JSON.stringify(ssiMessageTags), + ); + const connection = await PresentationService.requestReceived( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + unprocessedMessages[i].id, + unpackMessageResponse, ); - const connection = await PresentationService.requestReceived(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), unprocessedMessages[i].id, unpackMessageResponse); const event: EventInterface = { message: `You have received a proof request to verify from ${connection.theirLabel}`, type: 'Verify Proof', - messageData: JSON.stringify({ connection }) - } + messageData: JSON.stringify({connection}), + }; EventRegister.emit('SDKEvent', event); break; } case MessageType.problemReport: { - await WalletStorageService.deleteWalletRecord(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id) + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); break; } case MessageType.BasicMessage: { - const connection = await BasicMessageService.save(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), unpackMessageResponse, unpackMessageResponse.recipient_verkey); - if (connection) { await WalletStorageService.deleteWalletRecord(JSON.parse(this.wallet.walletConfig), JSON.parse(this.wallet.walletCredentials), RecordType.SSIMessage, unprocessedMessages[i].id) } - + const connection = await BasicMessageService.save( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + unpackMessageResponse, + unpackMessageResponse.recipient_verkey, + ); + if (connection) { + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); + } const event: EventInterface = { message: `You have received a message from ${connection.theirLabel}`, type: 'Message', connectionId: connection.verkey, - messageData: JSON.stringify({ connection }) - } + messageData: JSON.stringify({connection}), + }; EventRegister.emit('SDKEvent', event); break; } + + case MessageType.MediationGrant: { + const isCompleted = await MediatorService.saveRoutingKeys( + message, + ); + if (isCompleted) { + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); + } + break; + } + + case MessageType.Batch: { + await message['messages~attach'].map(async (message) => { + await this.addMessages(message.message); + }); + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); + break; + } + case MessageType.KeyListUpdateResponse: { + await WalletStorageService.deleteWalletRecord( + JSON.parse(this.wallet.walletConfig), + JSON.parse(this.wallet.walletCredentials), + RecordType.SSIMessage, + unprocessedMessages[i].id, + ); + + break; + } default: { break; } @@ -276,17 +499,21 @@ class InboundMessageHandler { } } this.isProcess = false; - EventRegister.emit('inboundMessageStatusListener', `inboundMessageStatusListener`); + EventRegister.emit( + 'inboundMessageStatusListener', + `inboundMessageStatusListener`, + ); return true; - } - catch (error) { - console.warn('Agent - Proceed inbound message error = ', error) + } catch (error) { + console.warn('Agent - Proceed inbound message error = ', error); this.isProcess = false; - EventRegister.emit('inboundMessageStatusListener', `inboundMessageStatusListener`); + EventRegister.emit( + 'inboundMessageStatusListener', + `inboundMessageStatusListener`, + ); throw error; } - } - + }; } -export default new InboundMessageHandler(); \ No newline at end of file +export default new InboundMessageHandler(); diff --git a/src/utils/DidDoc.ts b/src/utils/DidDoc.ts index 0ae23bf..b849f21 100644 --- a/src/utils/DidDoc.ts +++ b/src/utils/DidDoc.ts @@ -150,8 +150,8 @@ export class Service { constructor( id: string, serviceEndpoint: string, - recipientKeys: Verkey[] = [], - routingKeys: Verkey[] = [], + recipientKeys: string[] = [], + routingKeys: string[] = [], priority: number = 0, type: string ) { diff --git a/src/utils/Helpers.ts b/src/utils/Helpers.ts index a6ba5f7..b149dab 100644 --- a/src/utils/Helpers.ts +++ b/src/utils/Helpers.ts @@ -3,20 +3,24 @@ SPDX-License-Identifier: Apache-2.0 */ -import { Connection } from '../protocols/connection/ConnectionInterface'; -import { createForwardMessage } from '../protocols/connection/ConnectionMessages'; -import { InboundMessage, OutboundMessage } from './Types'; -import { InvitationDetails } from '../protocols/connection/InvitationInterface'; -import { Message } from './Types'; -import { NativeModules, Platform } from "react-native"; -import { OutboundAgentMessage } from '../network'; -import { WalletConfig, WalletCredentials } from '../wallet/WalletInterface'; +import {Connection, ConnectionProps} from '../protocols/connection/ConnectionInterface'; +import {createForwardMessage} from '../protocols/connection/ConnectionMessages'; +import {InboundMessage, OOBService, OutboundMessage} from './Types'; +import {InvitationDetails} from '../protocols/connection/InvitationInterface'; +import {Message} from './Types'; +import {NativeModules, Platform} from 'react-native'; +import {OutboundAgentMessage} from '../network'; +import {WalletConfig, WalletCredentials} from '../wallet/WalletInterface'; import base64url from 'base64url'; import DatabaseServices from '../storage'; +import MediatorService from '../protocols/mediator/MediatorService'; +import { ConnectionState } from '../protocols/connection/ConnectionState'; +import { Service } from './DidDoc'; const Buffer = require('buffer').Buffer; +global.Buffer = global.Buffer || require('buffer').Buffer; -const { ArnimaSdk } = NativeModules; +const {ArnimaSdk} = NativeModules; export enum RecordType { Connection = 'Connection', @@ -26,12 +30,12 @@ export enum RecordType { Presentation = 'Presentation', MediatorAgent = 'MediatorAgent', SSIMessage = 'SSIMessage', - Pool = 'Pool' + Pool = 'Pool', } function timestamp(): Uint8Array { let time = Date.now(); - const bytes = []; + const bytes: number[] = []; for (let i = 0; i < 8; i++) { const byte = time & 0xff; bytes.push(byte); @@ -40,10 +44,15 @@ function timestamp(): Uint8Array { return Uint8Array.from(bytes).reverse(); } -export async function verify(configJson: WalletConfig, credentialsJson: WalletCredentials, message: Message, field: string) { +export async function verify( + configJson: WalletConfig, + credentialsJson: WalletCredentials, + message: Message, + field: string, +) { try { - const fieldKey = `${field}~sig` - const { [fieldKey]: data, ...signedMessage } = message; + const fieldKey = `${field}~sig`; + const {[fieldKey]: data, ...signedMessage} = message; const signerVerkey = data.signer; const signedData = base64url.toBuffer(data.sig_data); @@ -51,18 +60,21 @@ export async function verify(configJson: WalletConfig, credentialsJson: WalletCr let valid; if (Platform.OS == 'android') { - valid = await ArnimaSdk.cryptoVerify(JSON.stringify(configJson), + valid = await ArnimaSdk.cryptoVerify( + JSON.stringify(configJson), JSON.stringify(credentialsJson), signerVerkey, Array.from(signedData), - Array.from(signature)); - } - else { - valid = await ArnimaSdk.cryptoVerify(JSON.stringify(configJson), + Array.from(signature), + ); + } else { + valid = await ArnimaSdk.cryptoVerify( + JSON.stringify(configJson), JSON.stringify(credentialsJson), signerVerkey, data.sig_data, - JSON.stringify(Array.from(signature))); + JSON.stringify(Array.from(signature)), + ); } // if (!valid) { @@ -77,29 +89,41 @@ export async function verify(configJson: WalletConfig, credentialsJson: WalletCr return originalMessage; } catch (error) { - console.log("verify = ", error); + console.log('verify = ', error); throw error; } } -export async function sign(configJson: WalletConfig, credentialsJson: WalletCredentials, signerVerkey: string, message: Message, field: string) { +export async function sign( + configJson: WalletConfig, + credentialsJson: WalletCredentials, + signerVerkey: string, + message: Message, + field: string, +) { try { + const {[field]: data, ...originalMessage} = message; - const { [field]: data, ...originalMessage } = message; - - const dataBuffer = Buffer.concat([timestamp(), Buffer.from(JSON.stringify(data), 'utf8')]); + const dataBuffer = Buffer.concat([ + timestamp(), + Buffer.from(JSON.stringify(data), 'utf8'), + ]); let signatureBuffer; if (Platform.OS === 'ios') { - signatureBuffer = await ArnimaSdk.cryptoSign(JSON.stringify(configJson), + signatureBuffer = await ArnimaSdk.cryptoSign( + JSON.stringify(configJson), JSON.stringify(credentialsJson), signerVerkey, - JSON.stringify(data)); + JSON.stringify(data), + ); } else { - signatureBuffer = await ArnimaSdk.cryptoSign(JSON.stringify(configJson), + signatureBuffer = await ArnimaSdk.cryptoSign( + JSON.stringify(configJson), JSON.stringify(credentialsJson), signerVerkey, - Array.from(dataBuffer)); + Array.from(dataBuffer), + ); } const signedMessage = { @@ -107,7 +131,8 @@ export async function sign(configJson: WalletConfig, credentialsJson: WalletCred '@id': message['@id'], ...originalMessage, [`${field}~sig`]: { - '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/signature/1.0/ed25519Sha512_single', + '@type': + 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/signature/1.0/ed25519Sha512_single', signature: base64url.encode(signatureBuffer), sig_data: base64url.encode(dataBuffer), signer: signerVerkey, @@ -116,39 +141,67 @@ export async function sign(configJson: WalletConfig, credentialsJson: WalletCred return signedMessage; } catch (error) { - console.log("sign message = ", error); + console.log('sign message = ', error); throw error; } } -export async function unpackMessage(configJson: WalletConfig, credentialsJson: WalletCredentials, inboundMessage: InboundMessage) { +export async function unpackMessage( + configJson: WalletConfig, + credentialsJson: WalletCredentials, + inboundMessage: InboundMessage, +) { try { + console.log('inboundMessage', inboundMessage); const buf = Buffer.from(JSON.stringify(inboundMessage)); let unpackedBufferMessage; if (Platform.OS === 'ios') { - unpackedBufferMessage = await ArnimaSdk.unpackMessage(JSON.stringify(configJson), JSON.stringify(credentialsJson), JSON.stringify(inboundMessage)) - } - else { - unpackedBufferMessage = await ArnimaSdk.unpackMessage(JSON.stringify(configJson), JSON.stringify(credentialsJson), Array.from(buf)) + unpackedBufferMessage = await ArnimaSdk.unpackMessage( + JSON.stringify(configJson), + JSON.stringify(credentialsJson), + JSON.stringify(inboundMessage), + ); + } else { + unpackedBufferMessage = await ArnimaSdk.unpackMessage( + JSON.stringify(configJson), + JSON.stringify(credentialsJson), + Array.from(buf), + ); } const unpackedMessage = Buffer.from(unpackedBufferMessage); + console.log('unpackedMessage', unpackedMessage.toString('utf-8')); return JSON.parse(unpackedMessage.toString('utf-8')); } catch (error) { - console.log("unpackMessage = ", error); + console.log('unpackMessage = ', error); throw error; } } -export async function packMessage(configJson: WalletConfig, credentialsJson: WalletCredentials, outboundMessage: OutboundMessage) { +export async function packMessage( + configJson: WalletConfig, + credentialsJson: WalletCredentials, + outboundMessage: OutboundMessage, +) { try { - const { routingKeys, recipientKeys, senderVk, payload } = outboundMessage; + const {routingKeys, recipientKeys, senderVk, payload} = outboundMessage; const buf = Buffer.from(JSON.stringify(payload)); let packedBufferMessage; if (Platform.OS === 'ios') { - packedBufferMessage = await ArnimaSdk.packMessage(JSON.stringify(configJson), JSON.stringify(credentialsJson), JSON.stringify(payload), recipientKeys, senderVk) - } - else { - packedBufferMessage = await ArnimaSdk.packMessage(JSON.stringify(configJson), JSON.stringify(credentialsJson), Array.from(buf), recipientKeys, senderVk) + packedBufferMessage = await ArnimaSdk.packMessage( + JSON.stringify(configJson), + JSON.stringify(credentialsJson), + JSON.stringify(payload), + recipientKeys, + senderVk, + ); + } else { + packedBufferMessage = await ArnimaSdk.packMessage( + JSON.stringify(configJson), + JSON.stringify(credentialsJson), + Array.from(buf), + recipientKeys, + senderVk, + ); } const packedMessage = Buffer.from(packedBufferMessage); const outboundPackedMessage = JSON.parse(packedMessage.toString('utf-8')); @@ -158,22 +211,34 @@ export async function packMessage(configJson: WalletConfig, credentialsJson: Wal for (const routingKey of routingKeys) { const [recipientKey] = recipientKeys; const forwardMessage = createForwardMessage(recipientKey, message); - const forwardMessageBuffer = Buffer.from(JSON.stringify(forwardMessage)); + const forwardMessageBuffer = Buffer.from( + JSON.stringify(forwardMessage), + ); let forwardBufferMessage; if (Platform.OS === 'ios') { - forwardBufferMessage = await ArnimaSdk.packMessage(JSON.stringify(configJson), JSON.stringify(credentialsJson), JSON.stringify(forwardMessage), [routingKey], senderVk) - } - else { - forwardBufferMessage = await ArnimaSdk.packMessage(JSON.stringify(configJson), JSON.stringify(credentialsJson), Array.from(forwardMessageBuffer), [routingKey], senderVk) + forwardBufferMessage = await ArnimaSdk.packMessage( + JSON.stringify(configJson), + JSON.stringify(credentialsJson), + JSON.stringify(forwardMessage), + [routingKey], + senderVk, + ); + } else { + forwardBufferMessage = await ArnimaSdk.packMessage( + JSON.stringify(configJson), + JSON.stringify(credentialsJson), + Array.from(forwardMessageBuffer), + [routingKey], + senderVk, + ); } const forwardPackedMessage = Buffer.from(forwardBufferMessage); message = JSON.parse(forwardPackedMessage.toString('utf-8')); } } return message; - } - catch (error) { - console.log("packMessage = ", error); + } catch (error) { + console.log('packMessage = ', error); throw error; } } @@ -181,19 +246,38 @@ export async function packMessage(configJson: WalletConfig, credentialsJson: Wal export function getServiceEndpoint() { // TODO : Need to find a way for realm db typing const sdkDB: any = DatabaseServices.getWallet(); - return `${sdkDB.serviceEndpoint.split("/")[0] + "/" + sdkDB.serviceEndpoint.split("/")[1] + "/" + sdkDB.serviceEndpoint.split("/")[2]}/`; + return `${ + sdkDB.serviceEndpoint.split('/')[0] + + '/' + + sdkDB.serviceEndpoint.split('/')[1] + + '/' + + sdkDB.serviceEndpoint.split('/')[2] + }/`; } export function decodeInvitationFromUrl(invitationUrl: string) { - const [, encodedInvitation] = invitationUrl.split('c_i='); - return JSON.parse(Buffer.from(encodedInvitation, 'base64').toString()); + if (invitationUrl.includes('?c_i=')) { + const [, encodedInvitation] = invitationUrl.split('c_i='); + return JSON.parse(Buffer.from(encodedInvitation, 'base64').toString()); + } else if (invitationUrl.includes('?d_m=')) { + const [encodedInvitation] = invitationUrl.split('=')[1].split('%'); + return JSON.parse(Buffer.from(encodedInvitation, 'base64').toString()); + } } export function encodeInvitationToUrl(invitation: InvitationDetails): string { - const encodedInvitation = Buffer.from(JSON.stringify(invitation)).toString('base64'); + const encodedInvitation = Buffer.from(JSON.stringify(invitation)).toString( + 'base64', + ); // TODO : Need to find a way for realm db typing const sdkDB: any = DatabaseServices.getWallet(); - return `${sdkDB.serviceEndpoint.split("/")[0] + "/" + sdkDB.serviceEndpoint.split("/")[1] + "/" + sdkDB.serviceEndpoint.split("/")[2]}/ssi?c_i=${encodedInvitation}`; + return `${ + sdkDB.serviceEndpoint.split('/')[0] + + '/' + + sdkDB.serviceEndpoint.split('/')[1] + + '/' + + sdkDB.serviceEndpoint.split('/')[2] + }/ssi?c_i=${encodedInvitation}`; } export function decodeBase64(base64Data: string) { @@ -204,10 +288,49 @@ export function encodeBase64(data: string) { return Buffer.from(JSON.stringify(data)).toString('base64'); } -export async function createOutboundMessage(connection: Connection, payload: Object, invitation?: Message, oobService?: object) { +function getServiceEndpointFromConnection(connection:ConnectionProps) { + const {theirDidDoc,state} = connection; + if (!theirDidDoc) { + throw new Error( + `DidDoc for connection with verkey ${connection.verkey} not found!`, + ); + } + const service = theirDidDoc?.service + + //Set the service endpoint to the one from the connection + let serviceEndpoint:string = service[0].serviceEndpoint + + //By default we should use the service endpoint from the connection with http protocol + //If the the service has websocket endpoint then use websocket service endpoint protocol + service.forEach(s => { + if(s.serviceEndpoint.includes('ws' || 'wss') && state === ConnectionState.COMPLETE){ + serviceEndpoint = s.serviceEndpoint + return; + } else if(s.serviceEndpoint.includes('http')){ + serviceEndpoint = s.serviceEndpoint + } + }); + + return serviceEndpoint +} + +export async function createOutboundMessage( + connection: Connection, + payload: Object, + invitation?: Message, + oobService?: OOBService, +) { if (connection) { + const shouldUseReturnRoute = Boolean( + connection.didDoc.service.find((s) => s.serviceEndpoint === ''), + ); + if (shouldUseReturnRoute) { + payload['~transport'] = { + return_route: 'all', + }; + } if (invitation) { - const { recipientKeys, routingKeys, serviceEndpoint } = invitation + const {recipientKeys, routingKeys, serviceEndpoint} = invitation; return { connection, endpoint: serviceEndpoint, @@ -218,37 +341,93 @@ export async function createOutboundMessage(connection: Connection, payload: Obj }; } - const { theirDidDoc } = connection; + const {theirDidDoc} = connection; if (!theirDidDoc) { - throw new Error(`DidDoc for connection with verkey ${connection.verkey} not found!`); + throw new Error( + `DidDoc for connection with verkey ${connection.verkey} not found!`, + ); } - const { service } = theirDidDoc + const {service} = theirDidDoc; + const serviceEndpoint = await getServiceEndpointFromConnection(connection) + return { connection, - endpoint: service[0].serviceEndpoint, + endpoint: serviceEndpoint, payload, recipientKeys: service[0].recipientKeys, routingKeys: service[0].routingKeys, senderVk: connection.verkey, }; } else { + + if(!oobService) { + throw new Error('No oobService provided') + } + const wallet = await DatabaseServices.getWallet(); - const [pairwiseDid, verkey]: string[] = await ArnimaSdk.createAndStoreMyDid( - wallet.walletConfig, wallet.walletCredentials, JSON.stringify({}), false); - const { recipientKeys, routingKeys, serviceEndpoint } = oobService; + const [, verkey]: string[] = await ArnimaSdk.createAndStoreMyDid( + wallet.walletConfig, + wallet.walletCredentials, + JSON.stringify({}), + false, + ); + const {recipientKeys, routingKeys, serviceEndpoint} = oobService; return { payload, recipientKeys, routingKeys, endpoint: serviceEndpoint, senderVk: verkey, - } + }; } } -export async function sendOutboundMessage(configJson: WalletConfig, credentialsJson: WalletCredentials, connection: Connection, message: Object, invitation?: Message, oobService?: object) { - const outboundMessage = await createOutboundMessage(connection, message, invitation, oobService); - const outboundPackMessage = await packMessage(configJson, credentialsJson, outboundMessage); - await OutboundAgentMessage(outboundMessage.endpoint, 'POST', JSON.stringify(outboundPackMessage)); +export async function sendOutboundMessage( + configJson: WalletConfig, + credentialsJson: WalletCredentials, + connection: Connection, + message: Object, + invitation?: Message, + oobService?: OOBService, +) { + const outboundMessage: OutboundMessage = await createOutboundMessage( + connection, + message, + invitation, + oobService, + ); + + const outboundPackMessage = await packMessage( + configJson, + credentialsJson, + outboundMessage, + ); + + if(outboundMessage.endpoint.includes('ws')){ + console.log("Websocket endpoint", outboundMessage.endpoint); + await MediatorService.sendWebSocketMessage(outboundMessage.endpoint, outboundPackMessage); + return; + } + + await OutboundAgentMessage( + outboundMessage.endpoint, + 'POST', + JSON.stringify(outboundPackMessage), + ); +} + +export function replaceDidSovPrefixOnMessage(message) { + message['@type'] = replaceDidSovPrefix(message['@type']); +} + +export function replaceDidSovPrefix(messageType: string) { + const didSovPrefix = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec'; + const didCommPrefix = 'https://didcomm.org'; + + if (messageType.startsWith(didCommPrefix)) { + return messageType.replace(didCommPrefix, didSovPrefix); + } + + return messageType; } diff --git a/src/utils/MessageType.ts b/src/utils/MessageType.ts index 4a58385..1aac742 100644 --- a/src/utils/MessageType.ts +++ b/src/utils/MessageType.ts @@ -30,5 +30,13 @@ export enum MessageType { Ack = 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/notification/1.0/ack', - problemReport = "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/notification/1.0/problem-report" + problemReport = "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/notification/1.0/problem-report", + + MediationRequest = "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/coordinate-mediation/1.0/mediate-request", + MediationGrant = "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/coordinate-mediation/1.0/mediate-grant", + KeyListUpdate = "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/coordinate-mediation/1.0/keylist-update", + KeyListUpdateResponse = "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/coordinate-mediation/1.0/keylist-update-response", + + BatchPickup = "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/messagepickup/1.0/batch-pickup", + Batch = "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/messagepickup/1.0/batch" } diff --git a/src/utils/Types.ts b/src/utils/Types.ts index 82724de..d5ae576 100644 --- a/src/utils/Types.ts +++ b/src/utils/Types.ts @@ -24,12 +24,12 @@ export interface InboundMessage { } export interface OutboundMessage { - connection: Connection; - endpoint?: string; + connection?: Connection; + endpoint: string; payload: Object; recipientKeys: Verkey[]; - routingKeys: Verkey[]; - senderVk: Verkey | null; + routingKeys?: Verkey[]; + senderVk: Verkey; } export interface OutboundPackage { @@ -42,3 +42,9 @@ export interface InboundConnection { verkey: Verkey; connection: Connection; } + +export interface OOBService { + recipientKeys: string[]; + routingKeys?: string[]; + serviceEndpoint: string; +} diff --git a/src/wallet/WalletService.ts b/src/wallet/WalletService.ts index a61fbc8..2ed36ae 100644 --- a/src/wallet/WalletService.ts +++ b/src/wallet/WalletService.ts @@ -52,32 +52,32 @@ class WalletService { verKey: verkey, masterSecretId: masterSecretId, }); + // TODO : Can remove if not required + // const walletRecord: WalletRecord = { + // walletConfig: config, + // walletCredentials: credentials, + // label: label, + // serviceEndpoint: "", + // routingKey: "", + // publicDid: did, + // verKey: verkey, + // masterSecretId: masterSecretId, + // } - const walletRecord: WalletRecord = { - walletConfig: config, - walletCredentials: credentials, - label: label, - serviceEndpoint: "", - routingKey: "", - publicDid: did, - verKey: verkey, - masterSecretId: masterSecretId, - } + // const walletRecordTags: Object = { + // walletName: label, + // publicDid: did, + // verKey: verkey, + // } - const walletRecordTags: Object = { - walletName: label, - publicDid: did, - verKey: verkey, - } - - await WalletStorageService.addWalletRecord( - config, - credentials, - RecordType.MediatorAgent, - '1', - JSON.stringify(walletRecord), - JSON.stringify(walletRecordTags) - ); + // await WalletStorageService.addWalletRecord( + // config, + // credentials, + // RecordType.MediatorAgent, + // '1', + // JSON.stringify(walletRecord), + // JSON.stringify(walletRecordTags) + // ); } return response; }