Skip to content

Commit

Permalink
perf: support for credential and present-proof flows with thid (#609)
Browse files Browse the repository at this point in the history
  • Loading branch information
Anton Baliasnikov committed Aug 3, 2023
1 parent 4fb1ca7 commit 9cef8c0
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 68 deletions.
6 changes: 2 additions & 4 deletions infrastructure/local/.env
@@ -1,7 +1,5 @@
MERCURY_MEDIATOR_VERSION=0.3.0
IRIS_SERVICE_VERSION=0.1.0
PRISM_AGENT_VERSION=1.6.0
PRISM_NODE_VERSION=v2.1.3
PRISM_AGENT_VERSION=1.9.2
PRISM_NODE_VERSION=2.2.1
PORT=80
NETWORK=prism
VAULT_DEV_ROOT_TOKEN_ID=root
Expand Up @@ -25,6 +25,7 @@
"start": "webpack"
},
"dependencies": {
"@input-output-hk/prism-typescript-client": "0.1.0"
"@input-output-hk/prism-typescript-client": "1.9.2",
"uuid": "^9.0.0"
}
}
Expand Up @@ -88,8 +88,8 @@ export class Holder extends Actor {
/**
* Waits for a credential offer and accepts it.
*/
waitAndAcceptCredentialOffer() {
this.credential = this.credentialsService.waitForCredentialOffer();
waitAndAcceptCredentialOffer(thid: string) {
this.credential = this.credentialsService.waitForCredentialOffer(thid);
this.credentialsService.acceptCredentialOffer(this.credential, this.did!);
this.credentialsService.waitForCredentialState(this.credential, "RequestSent");
}
Expand All @@ -104,8 +104,8 @@ export class Holder extends Actor {
/**
* Waits for a proof request, accepts it, and waits for the presentation to be sent.
*/
waitAndAcceptProofRequest() {
const presentation = this.proofsService.waitForProof();
waitAndAcceptProofRequest(thid: string) {
const presentation = this.proofsService.waitForProof(thid);
this.proofsService.acceptProofRequest(presentation, this.credential!.recordId);
this.proofsService.waitForPresentationState(presentation.presentationId, "PresentationSent");
}
Expand Down
@@ -1,4 +1,4 @@
import { Connection, IssueCredentialRecord } from "@input-output-hk/prism-typescript-client";
import { Connection, CredentialSchemaResponse, IssueCredentialRecord } from "@input-output-hk/prism-typescript-client";
import { Actor } from "./Actor";
import { ISSUER_AGENT_API_KEY, ISSUER_AGENT_URL } from "../common/Config";

Expand All @@ -19,6 +19,11 @@ export class Issuer extends Actor {
*/
credential: IssueCredentialRecord | undefined;

/**
* The schema for issued credential.
*/
schema: CredentialSchemaResponse | undefined;

/**
* The DID template used to create a DID for Issuer.
* assertionMethod is the only purpose for the public key required for Issuer.
Expand Down Expand Up @@ -74,11 +79,15 @@ export class Issuer extends Actor {
this.didService.waitForDidState(this.longFormDid!, "PUBLISHED");
}

createCredentialSchema() {
this.schema = this.credentialsService.createCredentialSchema(this.did!);
}

/**
* Creates a credential offer for the holder.
*/
createCredentialOffer() {
this.credential = this.credentialsService.createCredentialOffer(this.did!, this.connectionWithHolder!);
this.credential = this.credentialsService.createCredentialOffer(this.did!, this.connectionWithHolder!, this.schema!);
}

/**
Expand Down
@@ -1,4 +1,4 @@
import { Connection } from "@input-output-hk/prism-typescript-client";
import { Connection, PresentationStatus } from "@input-output-hk/prism-typescript-client";
import { Actor } from "./Actor";
import { VERIFIER_AGENT_API_KEY, VERIFIER_AGENT_URL } from "../common/Config";

Expand All @@ -12,7 +12,7 @@ export class Verifier extends Actor {
/**
* Presentation ID.
*/
presentationId: string | undefined;
presentation: PresentationStatus | undefined;

/**
* Creates a new instance of Verifier.
Expand Down Expand Up @@ -42,13 +42,14 @@ export class Verifier extends Actor {
* Requests proof from the holder.
*/
requestProof() {
this.presentationId = this.proofsService.requestProof(this.connectionWithHolder!);
let presentationId = this.proofsService.requestProof(this.connectionWithHolder!);
this.presentation = this.proofsService.getPresentation(presentationId);
}

/**
* Acknowledges the proof received from the holder.
*/
acknowledgeProof() {
this.proofsService.waitForPresentationState(this.presentationId!, "PresentationVerified");
this.proofsService.waitForPresentationState(this.presentation!.presentationId!, "PresentationVerified");
}
}
@@ -1,8 +1,8 @@
import { sleep } from "k6";
import { HttpService } from "./HttpService";
import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config";
import { IssueCredentialRecord, Connection } from "@input-output-hk/prism-typescript-client";
import vu from "k6/execution";
import { ISSUER_AGENT_URL, WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config";
import { IssueCredentialRecord, Connection, CredentialSchemaResponse } from "@input-output-hk/prism-typescript-client";
import {v4 as uuidv4} from 'uuid';

/**
* A service class for managing credentials in the application.
Expand All @@ -16,9 +16,16 @@ export class CredentialsService extends HttpService {
* @param {Connection} connection - The connection object.
* @returns {IssueCredentialRecord} The created credential offer record.
*/
createCredentialOffer(issuingDid: string, connection: Connection): IssueCredentialRecord {
createCredentialOffer(issuingDid: string, connection: Connection, schema: CredentialSchemaResponse): IssueCredentialRecord {
const payload = `{
"claims": { "offerId": "${vu.vu.idInInstance}-${vu.vu.idInTest}-${vu.vu.iterationInScenario}" },
"claims": {
"emailAddress": "${uuidv4()}-@atala.io",
"familyName": "Test",
"dateOfIssuance": "${new Date()}",
"drivingLicenseID": "Test",
"drivingClass": 1
},
"schemaId": "${ISSUER_AGENT_URL.replace("localhost", "host.docker.internal")}/schema-registry/schemas/${schema.guid}",
"issuingDID": "${issuingDid}",
"connectionId": "${connection.connectionId}",
"automaticIssuance": false
Expand All @@ -27,6 +34,60 @@ export class CredentialsService extends HttpService {
return res.json() as unknown as IssueCredentialRecord;
}

createCredentialSchema(issuingDid: string): CredentialSchemaResponse {
const payload = `
{
"name": "${uuidv4()}}",
"version": "1.0.0",
"description": "Simple credential schema for the driving licence verifiable credential.",
"type": "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json",
"schema": {
"$id": "https://example.com/driving-license-1.0",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "Driving License",
"type": "object",
"properties": {
"emailAddress": {
"type": "string",
"format": "email"
},
"givenName": {
"type": "string"
},
"familyName": {
"type": "string"
},
"dateOfIssuance": {
"type": "string"
},
"drivingLicenseID": {
"type": "string"
},
"drivingClass": {
"type": "integer"
}
},
"required": [
"emailAddress",
"familyName",
"dateOfIssuance",
"drivingLicenseID",
"drivingClass"
],
"additionalProperties": false
},
"tags": [
"driving",
"licence",
"id"
],
"author": "${issuingDid}"
}
`
const res = this.post("schema-registry/schemas", payload);
return res.json() as unknown as CredentialSchemaResponse;
}

/**
* Retrieves a specific credential record by ID.
* @param {IssueCredentialRecord} record - The credential record.
Expand All @@ -41,8 +102,8 @@ export class CredentialsService extends HttpService {
* Retrieves all credential records.
* @returns {IssueCredentialRecord[]} An array of credential records.
*/
getCredentialRecords(): IssueCredentialRecord[] {
const res = this.get("issue-credentials/records");
getCredentialRecords(thid: string): IssueCredentialRecord[] {
const res = this.get(`issue-credentials/records?thid=${thid}`);
return res.json("contents") as unknown as IssueCredentialRecord[];
}

Expand Down Expand Up @@ -73,21 +134,21 @@ export class CredentialsService extends HttpService {
* @returns {IssueCredentialRecord} The received credential offer record.
* @throws {Error} If the credential offer is not received within the maximum iterations.
*/
waitForCredentialOffer(): IssueCredentialRecord {
waitForCredentialOffer(thid: string): IssueCredentialRecord {
let iterations = 0;
let record: IssueCredentialRecord | undefined;
do {
console.log(`${vu.vu.idInInstance}-${vu.vu.idInTest}-${vu.vu.iterationInScenario}`)
record = this.getCredentialRecords().find(
r => r.claims["offerId"] === `${vu.vu.idInInstance}-${vu.vu.idInTest}-${vu.vu.iterationInScenario}`
&& r.protocolState === "OfferReceived");
// console.log(`Waiting for credential offer with thid=${thid}`)
record = this.getCredentialRecords(thid).find(
r => r.thid === thid && r.protocolState === "OfferReceived"
);
if (record) {
return record;
}
sleep(WAITING_LOOP_PAUSE_INTERVAL);
iterations++;
} while (iterations < WAITING_LOOP_MAX_ITERATIONS);
throw new Error(`Record with offerId=${vu.vu.idInTest} not achieved during the waiting loop`);
throw new Error(`Record with thid=${thid} not achieved during the waiting loop`);
}

/**
Expand All @@ -102,14 +163,12 @@ export class CredentialsService extends HttpService {
do {
const response = this.getCredentialRecord(credentialRecord);
currentState = response.protocolState;
console.log(`Credential state: ${currentState}`)
sleep(WAITING_LOOP_PAUSE_INTERVAL);
iterations++;
} while (currentState !== state && iterations < WAITING_LOOP_MAX_ITERATIONS);
if (currentState !== state) {
throw new Error(`Credential is not ${state} after the waiting loop`);
}
if (__ENV.DEBUG) console.log(`Credential state achieved: ${currentState}`);
}

}
Expand Up @@ -68,8 +68,8 @@ export class ProofsService extends HttpService {
* Retrieves all presentations.
* @returns {PresentationStatus[]} An array of presentation status objects.
*/
getPresentations(): PresentationStatus[] {
const res = this.get(`present-proof/presentations`);
getPresentations(thid: string): PresentationStatus[] {
const res = this.get(`present-proof/presentations?thid=${thid}`);
return res.json("contents") as unknown as PresentationStatus[];
}

Expand All @@ -78,13 +78,13 @@ export class ProofsService extends HttpService {
* @returns {PresentationStatus} The received presentation status object.
* @throws {Error} If the proof request is not received within the maximum iterations.
*/
waitForProof(): PresentationStatus {
waitForProof(thid: string): PresentationStatus {
let iterations = 0;
let presentation: PresentationStatus | undefined;
do {
// TODO: add correct filtering here when the API is fixed
// see ATL-4665
presentation = this.getPresentations().find(r => r.status === "RequestReceived");
presentation = this.getPresentations(thid).find(
r => r.thid === thid && r.status === "RequestReceived"
);
if (presentation) {
return presentation;
}
Expand All @@ -105,11 +105,10 @@ export class ProofsService extends HttpService {
let state: string;
do {
state = this.getPresentation(presentationId).status;
if (__ENV.DEBUG) console.log(`Presentation state: ${state}, required: ${requiredState}`);
sleep(WAITING_LOOP_PAUSE_INTERVAL);
iterations++;
} while (state !== requiredState && iterations < WAITING_LOOP_MAX_ITERATIONS);
if (state != requiredState) {
if (state !== requiredState) {
throw new Error(`Presentation state is ${state}, required ${requiredState}`);
}
}
Expand Down
@@ -0,0 +1,43 @@
import { group } from 'k6';
import { Options } from 'k6/options';
import { Issuer } from '../../actors';

export let options: Options = {
scenarios: {
smoke: {
executor: 'constant-vus',
vus: 3,
duration: "1s",
},
},
thresholds: {
'http_req_duration{group:::Issuer creates credential schema}': ['max >= 0'],
'http_reqs{group:::Issuer creates credential schema}': ['count >= 0'],
'group_duration{group:::Issuer creates credential schema}': ['max >= 0'],
checks: ['rate==1'],
http_req_duration: ['p(95)<=100'],
},
};

export const issuer = new Issuer();

export function setup() {
group('Issuer publishes DID', function () {
issuer.createUnpublishedDid();
issuer.publishDid();
});

return {
issuerDid: issuer.did,
};
}

export default (data: { issuerDid: string; }) => {

// This is the only way to pass data from setup to default
issuer.did = data.issuerDid;

group('Issuer creates credential schema', function () {
issuer.createCredentialSchema();
});
};

0 comments on commit 9cef8c0

Please sign in to comment.