diff --git a/example/src/credo/agent.ts b/example/src/credo/agent.ts index cfd206f..6db49bc 100644 --- a/example/src/credo/agent.ts +++ b/example/src/credo/agent.ts @@ -36,7 +36,7 @@ export const setupAgent = async () => { id: 'react-native-ble-didcomm-agent', key: 'react-native-ble-didcomm-key', }, - logger: new ConsoleLogger(LogLevel.info), + logger: new ConsoleLogger(LogLevel.off), }, modules: { askar: new AskarModule({ ariesAskar }), diff --git a/library/src/bleRequestProof.ts b/library/src/bleRequestProof.ts index afe5f53..20a0456 100644 --- a/library/src/bleRequestProof.ts +++ b/library/src/bleRequestProof.ts @@ -2,6 +2,7 @@ import type { AnonCredsProofFormat } from '@credo-ts/anoncreds' import { type Agent, AutoAcceptProof, + JsonTransformer, MessageReceiver, ProofEventTypes, type ProofExchangeRecord, @@ -54,10 +55,9 @@ export const bleRequestProof = async ({ ) const messageListener = startMessageReceiver(agent, peripheral) - await returnWhenProofReceived(proofRecordId, agent) + await returnWhenProofReceived(proofRecordId, agent, peripheral) messageListener.remove() - await shutdownProcess(agent, peripheral) return proofRecordId } catch (e) { if (e instanceof Error) { @@ -66,7 +66,6 @@ export const bleRequestProof = async ({ agent.config.logger.error(e as string) } onFailure() - await shutdownProcess(agent, peripheral) throw e } } @@ -90,16 +89,6 @@ const startPeripheral = async (peripheral: Peripheral, agent: Agent, serviceUuid agent.config.logger.info(`[PERIPHERAL]: Advertising on service UUID '${serviceUuid}'`) } -const shutdownProcess = async (agent: Agent, peripheral: Peripheral) => { - for (const ot of agent.outboundTransports) { - if (ot instanceof BleOutboundTransport) { - void agent.unregisterOutboundTransport(ot) - } - } - - await peripheral.shutdown() -} - const sendMessageWhenConnected = async ( agent: Agent, peripheral: Peripheral, @@ -138,17 +127,24 @@ const startMessageReceiver = (agent: Agent, peripheral: Peripheral) => { }) } -const returnWhenProofReceived = (id: string, agent: Agent): Promise => { +const returnWhenProofReceived = (id: string, agent: Agent, peripheral: Peripheral): Promise => { return new Promise((resolve, reject) => { - const listener = ({ payload }: ProofStateChangedEvent) => { + const listener = async ({ payload: { proofRecord } }: ProofStateChangedEvent) => { const off = () => agent.events.off(ProofEventTypes.ProofStateChanged, listener) - if (payload.proofRecord.id === id) { - if (payload.proofRecord.state === ProofState.PresentationReceived) { + if (proofRecord.id === id) { + if (proofRecord.state === ProofState.PresentationReceived) { + console.log('') + const proofProtocol = agent.proofs.config.proofProtocols.find((pp) => pp.version === 'v2') + if (!proofProtocol) throw new Error('No V2 proof protocol registered on the agent') + const { message } = await proofProtocol.acceptPresentation(agent.context, { proofRecord }) + const serializedMessage = JsonTransformer.serialize(message) + await peripheral.sendMessage(serializedMessage) + } else if (proofRecord.state === ProofState.Done) { off() - resolve(payload.proofRecord) - } else if ([ProofState.Abandoned, ProofState.Declined].includes(payload.proofRecord.state)) { + resolve(proofRecord) + } else if ([ProofState.Abandoned, ProofState.Declined].includes(proofRecord.state)) { off() - reject(new Error(`Proof could not be shared because it has been ${payload.proofRecord.state}`)) + reject(new Error(`Proof could not be shared because it has been ${proofRecord.state}`)) } } } @@ -164,7 +160,7 @@ const createBleProofRequestMessage = async ( const { proofRecord, message } = await agent.proofs.createRequest({ proofFormats: requestMessage, protocolVersion: 'v2', - autoAcceptProof: AutoAcceptProof.Always, + autoAcceptProof: AutoAcceptProof.Never, }) const routing = await agent.mediationRecipient.getRouting({ diff --git a/library/src/bleShareProof.ts b/library/src/bleShareProof.ts index b1934c7..9e169b7 100644 --- a/library/src/bleShareProof.ts +++ b/library/src/bleShareProof.ts @@ -7,6 +7,7 @@ import { ProofRepository, ProofState, type ProofStateChangedEvent, + V2PresentationAckMessage, } from '@credo-ts/core' import { BleInboundTransport, BleOutboundTransport } from '@credo-ts/transport-ble' import type { Central } from './central' @@ -43,8 +44,8 @@ export const bleShareProof = async ({ await connectedNotifier(agent, central, onConnected) - await shareProof(agent, central, serviceUuid) - await shutdownProcess(agent, central) + const proofRecord = await shareProof(agent, central, serviceUuid) + await handleAck(agent, central, proofRecord) } catch (e) { if (e instanceof Error) { agent.config.logger.error(e.message, { cause: e }) @@ -53,7 +54,6 @@ export const bleShareProof = async ({ } onFailure() - await shutdownProcess(agent, central) throw e } } @@ -108,31 +108,13 @@ const disconnctedNotifier = (agent: Agent, central: Central, onDisconnected?: () }) } -const shutdownProcess = async (agent: Agent, central: Central) => { - for (const it of agent.inboundTransports) { - if (it instanceof BleInboundTransport) { - void agent.unregisterInboundTransport(it) - } - } - - for (const ot of agent.outboundTransports) { - if (ot instanceof BleOutboundTransport) { - void agent.unregisterOutboundTransport(ot) - } - } - - await central.shutdown() -} - const shareProof = async (agent: Agent, central: Central, serviceUuid: string) => - new Promise((resolve) => { + new Promise((resolve) => { const receivedMessageListener = central.registerMessageListener(async ({ message }) => { agent.config.logger.info(`[CENTRAL]: received message ${message.slice(0, 16)}...`) const parsedMessage = JsonTransformer.deserialize(message, OutOfBandInvitation) - const responder = autoRespondToBleProofRequest(agent) - const routing = await agent.mediationRecipient.getRouting({ useDefaultMediator: false, }) @@ -141,18 +123,17 @@ const shareProof = async (agent: Agent, central: Central, serviceUuid: string) = routing: { ...routing, endpoints: [`ble://${serviceUuid}`] }, }) - const { id } = await responder - - await waitForSharedProof(id, agent) + const proofExchangeRecord = await autoRespondToBleProofRequest(agent) receivedMessageListener.remove() - resolve() + resolve(proofExchangeRecord) }) }) const autoRespondToBleProofRequest = (agent: Agent): Promise => { return new Promise((resolve, reject) => { const listener = async ({ payload: { proofRecord } }: ProofStateChangedEvent) => { + const off = () => agent.events.off(ProofEventTypes.ProofStateChanged, listener) if (proofRecord.state === ProofState.RequestReceived) { const formatData = await agent.proofs.getFormatData(proofRecord.id) @@ -162,34 +143,29 @@ const autoRespondToBleProofRequest = (agent: Agent): Promise(ProofEventTypes.ProofStateChanged, listener) }) } -const waitForSharedProof = (id: string, agent: Agent): Promise => - new Promise((resolve, reject) => { - const listener = ({ payload }: ProofStateChangedEvent) => { - const off = () => agent.events.off(ProofEventTypes.ProofStateChanged, listener) - if (payload.proofRecord.id === id) { - if (payload.proofRecord.state === ProofState.PresentationReceived) { - off() - resolve(payload.proofRecord) - } else if ([ProofState.Abandoned, ProofState.Declined].includes(payload.proofRecord.state)) { - off() - reject(new Error(`Proof could not be shared because it has been ${payload.proofRecord.state}`)) - } - } - } - agent.events.on(ProofEventTypes.ProofStateChanged, listener) +const handleAck = async (agent: Agent, central: Central, proofRecord: ProofExchangeRecord) => + new Promise((resolve) => { + const listener = central.registerMessageListener(async ({ message }) => { + if (!message.includes('@type')) throw new Error(`Received invalid message '${message}'`) + + const deserializedMessage = JsonTransformer.deserialize(message, V2PresentationAckMessage) + if (deserializedMessage.threadId !== proofRecord.threadId) throw new Error('Received Ack with invalid thread id') + + const proofRepository = agent.dependencyManager.resolve(ProofRepository) + proofRecord.state = ProofState.Done + const formatData = await agent.proofs.getFormatData(proofRecord.id) + proofRecord.metadata.set(METADATA_KEY_FORMAT_DATA, formatData) + await proofRepository.update(agent.context, proofRecord) + + listener.remove() + resolve() + }) })