Skip to content

Commit

Permalink
feat: allow sending problem report when declining a proof request (#1408
Browse files Browse the repository at this point in the history
)

Signed-off-by: Artemkaaas <[email protected]>
  • Loading branch information
Artemkaaas authored Apr 10, 2023
1 parent ec5c233 commit b35fec4
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 21 deletions.
2 changes: 1 addition & 1 deletion demo/src/AliceInquirer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class AliceInquirer extends BaseInquirer {
public async acceptProofRequest(proofRecord: ProofExchangeRecord) {
const confirm = await prompt([this.inquireConfirmation(Title.ProofRequestTitle)])
if (confirm.options === ConfirmOptions.No) {
await this.alice.agent.proofs.declineRequest(proofRecord.id)
await this.alice.agent.proofs.declineRequest({ proofRecordId: proofRecord.id })
} else if (confirm.options === ConfirmOptions.Yes) {
await this.alice.acceptProofRequest(proofRecord)
}
Expand Down
68 changes: 52 additions & 16 deletions packages/core/src/modules/proofs/ProofsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
SelectCredentialsForProofRequestOptions,
SelectCredentialsForProofRequestReturn,
SendProofProblemReportOptions,
DeclineProofRequestOptions,
} from './ProofsApiOptions'
import type { ProofProtocol } from './protocol/ProofProtocol'
import type { ProofFormatsFromProtocols } from './protocol/ProofProtocolOptions'
Expand Down Expand Up @@ -49,7 +50,7 @@ export interface ProofsApi<PPs extends ProofProtocol[]> {
// Request methods
requestProof(options: RequestProofOptions<PPs>): Promise<ProofExchangeRecord>
acceptRequest(options: AcceptProofRequestOptions<PPs>): Promise<ProofExchangeRecord>
declineRequest(proofRecordId: string): Promise<ProofExchangeRecord>
declineRequest(options: DeclineProofRequestOptions): Promise<ProofExchangeRecord>
negotiateRequest(options: NegotiateProofRequestOptions<PPs>): Promise<ProofExchangeRecord>

// Present
Expand Down Expand Up @@ -368,11 +369,15 @@ export class ProofsApi<PPs extends ProofProtocol[]> implements ProofsApi<PPs> {
}
}

public async declineRequest(proofRecordId: string): Promise<ProofExchangeRecord> {
const proofRecord = await this.getById(proofRecordId)
public async declineRequest(options: DeclineProofRequestOptions): Promise<ProofExchangeRecord> {
const proofRecord = await this.getById(options.proofRecordId)
proofRecord.assertState(ProofState.RequestReceived)

const protocol = this.getProtocol(proofRecord.protocolVersion)
if (options.sendProblemReport) {
await this.sendProblemReport({ proofRecordId: options.proofRecordId, description: 'Request declined' })
}

await protocol.updateState(this.agentContext, proofRecord, ProofState.Declined)

return proofRecord
Expand Down Expand Up @@ -555,29 +560,60 @@ export class ProofsApi<PPs extends ProofProtocol[]> implements ProofsApi<PPs> {
*/
public async sendProblemReport(options: SendProofProblemReportOptions): Promise<ProofExchangeRecord> {
const proofRecord = await this.getById(options.proofRecordId)
if (!proofRecord.connectionId) {
throw new AriesFrameworkError(`No connectionId found for proof record '${proofRecord.id}'.`)
}

const protocol = this.getProtocol(proofRecord.protocolVersion)
const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId)

// Assert
connectionRecord.assertReady()
const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id)

const { message: problemReport } = await protocol.createProblemReport(this.agentContext, {
proofRecord,
description: options.description,
})

const outboundMessageContext = new OutboundMessageContext(problemReport, {
agentContext: this.agentContext,
connection: connectionRecord,
associatedRecord: proofRecord,
})
if (proofRecord.connectionId) {
const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId)

await this.messageSender.sendMessage(outboundMessageContext)
return proofRecord
// Assert
connectionRecord.assertReady()

const outboundMessageContext = new OutboundMessageContext(problemReport, {
agentContext: this.agentContext,
connection: connectionRecord,
associatedRecord: proofRecord,
})

await this.messageSender.sendMessage(outboundMessageContext)
return proofRecord
} else if (requestMessage?.service) {
proofRecord.assertState(ProofState.RequestReceived)

// Create ~service decorator
const routing = await this.routingService.getRouting(this.agentContext)
const ourService = new ServiceDecorator({
serviceEndpoint: routing.endpoints[0],
recipientKeys: [routing.recipientKey.publicKeyBase58],
routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58),
})
const recipientService = requestMessage.service

await this.messageSender.sendMessageToService(
new OutboundMessageContext(problemReport, {
agentContext: this.agentContext,
serviceParams: {
service: recipientService.resolvedDidCommService,
senderKey: ourService.resolvedDidCommService.recipientKeys[0],
},
})
)

return proofRecord
}
// Cannot send message without connectionId or ~service decorator
else {
throw new AriesFrameworkError(
`Cannot send problem report without connectionId or ~service decorator on presentation request.`
)
}
}

public async getFormatData(proofRecordId: string): Promise<GetProofFormatDataReturn<ProofFormatsFromProtocols<PPs>>> {
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/modules/proofs/ProofsApiOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,11 @@ export interface SendProofProblemReportOptions {
proofRecordId: string
description: string
}

/**
* Interface for ProofsApi.declineRequest. Decline a received proof request and optionally send a problem-report message to Verifier
*/
export interface DeclineProofRequestOptions {
proofRecordId: string
sendProblemReport?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,14 @@ export abstract class BaseProofProtocol<PFs extends ProofFormatService[] = Proof
public async processProblemReport(
messageContext: InboundMessageContext<ProblemReportMessage>
): Promise<ProofExchangeRecord> {
const { message: proofProblemReportMessage, agentContext } = messageContext

const connection = messageContext.assertReadyConnection()
const { message: proofProblemReportMessage, agentContext, connection } = messageContext

agentContext.config.logger.debug(`Processing problem report with message id ${proofProblemReportMessage.id}`)

const proofRecord = await this.getByThreadAndConnectionId(
agentContext,
proofProblemReportMessage.threadId,
connection.id
connection?.id
)

// Update record
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
makeConnection,
testLogger,
setupEventReplaySubjects,
waitForProofExchangeRecord,
} from '../../../../../../tests'
import { Agent } from '../../../../../agent/Agent'
import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment'
Expand Down Expand Up @@ -543,4 +544,98 @@ describe('V2 Connectionless Proofs - Indy', () => {
threadId: requestMessage.threadId,
})
})

test('Faber starts with connection-less proof requests to Alice but gets Problem Reported', async () => {
testLogger.test('Faber sends presentation request to Alice')

const {
issuerAgent: faberAgent,
issuerReplay: faberReplay,
holderAgent: aliceAgent,
holderReplay: aliceReplay,
credentialDefinitionId,
issuerHolderConnectionId: faberConnectionId,
} = await setupAnonCredsTests({
issuerName: 'Faber connection-less Proofs v2 - Reject Request',
holderName: 'Alice connection-less Proofs v2 - Reject Request',
autoAcceptProofs: AutoAcceptProof.Never,
attributeNames: ['name', 'age'],
})

await issueLegacyAnonCredsCredential({
issuerAgent: faberAgent,
issuerReplay: faberReplay,
holderAgent: aliceAgent,
holderReplay: aliceReplay,
issuerHolderConnectionId: faberConnectionId,
offer: {
credentialDefinitionId,
attributes: [
{
name: 'name',
value: 'Alice',
},
{
name: 'age',
value: '99',
},
],
},
})

agents = [aliceAgent, faberAgent]

// eslint-disable-next-line prefer-const
// eslint-disable-next-line prefer-const
let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({
protocolVersion: 'v2',
proofFormats: {
indy: {
name: 'test-proof-request',
version: '1.0',
requested_attributes: {
name: {
name: 'name',
restrictions: [
{
cred_def_id: credentialDefinitionId,
},
],
},
},
requested_predicates: {},
},
},
autoAcceptProof: AutoAcceptProof.ContentApproved,
})

const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({
recordId: faberProofExchangeRecord.id,
message,
domain: 'rxjs:faber',
})

for (const transport of faberAgent.outboundTransports) {
await faberAgent.unregisterOutboundTransport(transport)
}

const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, {
state: ProofState.RequestReceived,
})

await aliceAgent.receiveMessage(requestMessage.toJSON())
const aliceProofExchangeRecord = await aliceProofExchangeRecordPromise

await aliceAgent.proofs.declineRequest({ proofRecordId: aliceProofExchangeRecord.id, sendProblemReport: true })

await waitForProofExchangeRecordSubject(aliceReplay, {
state: ProofState.Declined,
threadId: requestMessage.threadId,
})

await waitForProofExchangeRecordSubject(faberReplay, {
state: ProofState.Abandoned,
threadId: requestMessage.threadId,
})
})
})

0 comments on commit b35fec4

Please sign in to comment.