Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✅ K6: revocation scenarios #993

Merged
merged 15 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions resources/postman-prf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
```mermaid
graph TD
%% Define styles for different actors with improved contrast
classDef tenantAdmin fill:#99ff99,stroke:#333,stroke-width:2px,color:#000000,font-weight:bold;
classDef governance fill:#ff9999,stroke:#333,stroke-width:2px,color:#000000,font-weight:bold;
classDef issuerTenant fill:#9999ff,stroke:#333,stroke-width:2px,color:#000000,font-weight:bold;
classDef holderTenant fill:#ffff99,stroke:#333,stroke-width:2px,color:#000000,font-weight:bold;
classDef publicEndpoint fill:#d3d3d3,stroke:#333,stroke-width:2px,color:#000000,font-weight:bold;
classDef assertion fill:#ffa500,stroke:#333,stroke-width:2px,color:#000000,font-weight:bold;
%% Define styles for SSE steps
classDef sseIssuer stroke:#9999ff,stroke-width:4px,color:#1;
classDef sseHolder stroke:#ffff99,stroke-width:4px,color:#1;
%% Start and End
Start[Start]
End[End]
%% Tenant Admin actions
GetIssuerTenant[Get issuer tenant]:::tenantAdmin
GetIssuerToken[Get issuer access token]:::tenantAdmin
CreateIssuer[Create issuer/verifier]:::tenantAdmin
GetHolderTenant[Get holder tenant]:::tenantAdmin
GetHolderToken[Get holder access token]:::tenantAdmin
CreateHolder[Create holder]:::tenantAdmin
DeleteIssuer[Delete issuer/verifier]:::tenantAdmin
DeleteHolder[Delete holder]:::tenantAdmin
%% Governance actions
GetSchema[Get Schema]:::governance
CreateSchema[Create Schema]:::governance
%% Public actions
VerifyIssuerRegistry[Verify issuer/verifier on trust-registry]:::publicEndpoint
VerifySchemaRegistry[Verify schema on trust-registry]:::publicEndpoint
%% Issuer Tenant actions
CreateInvitation[Create invitation]:::issuerTenant
GetCredDef[Get Cred Def]:::issuerTenant
CreateCredDef[Create Cred Def]:::issuerTenant
CreateCredential[Create Credential]:::issuerTenant
ListCredentials[List credentials]:::holderTenant
SendProofRequest[Send Proof Request]:::issuerTenant
GetProof[Get Proof]:::issuerTenant
RevokeCredential[Revoke Credential]:::issuerTenant
CheckRevoked[Check Revoked]:::issuerTenant
%% Holder Tenant actions
AcceptInvitation[Accept invitation]:::holderTenant
WaitOfferReceived[Wait for SSE offer-received]:::sseHolder
GetCredentialsID[Get Credentials ID]:::holderTenant
AcceptCredential[Accept Credential]:::holderTenant
WaitRequestReceived[Wait for SSE request-received]:::sseHolder
GetProofID[Get Proof ID]:::holderTenant
GetReferent[Get Referent]:::holderTenant
AcceptProofRequest[Accept Proof Request]:::holderTenant
%% Shared or system actions
WaitConnectionReady[Wait for SSE connection-ready]:::sseHolder
WaitProofDone[Wait for SSE proof done]:::sseIssuer
WaitInvitationSent[Wait for SSE invitation-sent]:::sseIssuer
%% Assertions
AssertVerifiedTrue>Assert: verified = true]:::assertion
AssertVerifiedFalse>Assert: verified = false]:::assertion
%% Flow
Start --> GetIssuerTenant
GetIssuerTenant -->|Issuer exists| GetIssuerToken
GetIssuerTenant -->|Issuer doesn't exist| CreateIssuer --> VerifyIssuerRegistry --> GetHolderTenant
GetIssuerToken --> GetHolderTenant
GetHolderTenant -->|Holder exists| GetHolderToken
GetHolderTenant -->|Holder doesn't exist| CreateHolder --> CreateInvitation
GetHolderToken --> CreateInvitation --> WaitInvitationSent --> AcceptInvitation --> WaitConnectionReady --> GetSchema
GetSchema -->|Schema exists| VerifySchemaRegistry
GetSchema -->|Schema doesn't exist| CreateSchema --> VerifySchemaRegistry
VerifySchemaRegistry --> GetCredDef
GetCredDef -->|Cred Def exists| CreateCredential
GetCredDef -->|Cred Def doesn't exist| CreateCredDef --> CreateCredential
CreateCredential --> WaitOfferReceived --> GetCredentialsID --> AcceptCredential --> ListCredentials
ListCredentials --> SendProofRequest --> WaitRequestReceived --> GetProofID --> GetReferent --> AcceptProofRequest --> WaitProofDone --> GetProof
GetProof -->|Is revokedFlag true| AssertVerifiedFalse
GetProof -->|Is revokedFlag false| AssertVerifiedTrue
AssertVerifiedTrue --> RevokeCredential --> CheckRevoked -->|Set revokedFlag = true| SendProofRequest
AssertVerifiedFalse -->|Is deleteTenantFlag true| DeleteIssuer --> DeleteHolder --> End
AssertVerifiedFalse -->|Is deleteTenantFlag false| End
%% Legend
subgraph Legend
TenantAdmin[Tenant Admin]:::tenantAdmin
Governance[Governance]:::governance
IssuerTenant[Issuer Tenant]:::issuerTenant
HolderTenant[Holder Tenant]:::holderTenant
PublicEndpoint[Public]:::publicEndpoint
end
```
72 changes: 68 additions & 4 deletions scripts/k6/libs/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { check, sleep } from "k6";
import http from "k6/http";
import { Counter, Trend } from "k6/metrics";
import sse from "k6/x/sse";
// import { sleep } from 'k6';

// let customDuration = new Trend('custom_duration', true);

function logError(response, requestBody) {
Expand Down Expand Up @@ -191,6 +189,7 @@ export function createIssuerTenant(bearerToken, walletName) {
console.warn(`Request failed for wallet_name ${walletName}`);
return null;
} catch (error) {
logError(response);
console.error(`Error creating issuer tenant: ${error.message}`);
throw error;
}
Expand Down Expand Up @@ -516,12 +515,17 @@ export function sendProofRequest(issuerAccessToken, issuerConnectionId) {
"Content-Type": "application/json",
},
};

try {
// Get current epoch time in seconds
const currentEpochTimeSeconds = Math.floor(Date.now() / 1000);

// Construct the request body including the invitation object
const requestBody = {
type: "indy",
indy_proof_request: {
non_revoked: {
to: currentEpochTimeSeconds, // Current epoch time in seconds
},
requested_attributes: {
get_id_number: { name: "id_number" },
},
Expand All @@ -532,7 +536,6 @@ export function sendProofRequest(issuerAccessToken, issuerConnectionId) {
protocol_version: "v2",
connection_id: issuerConnectionId,
};

const response = http.post(url, JSON.stringify(requestBody), params);
return response;
} catch (error) {
Expand Down Expand Up @@ -645,6 +648,7 @@ export function getProofIdCredentials(holderAccessToken, proofId) {
const obj = responseData[i];
// Check if the current object has a matching thread_id
const referent = obj.cred_info.referent;
// TODO: this will always return the first referent - fix this
return referent;
}
// Throw an error if no match is found
Expand Down Expand Up @@ -781,6 +785,22 @@ export function getProof(issuerAccessToken, issuerConnectionId, proofThreadId) {
}
}

export function getDocs() {
const url = `${__ENV.CLOUDAPI_URL}/tenant-admin/docs`;
const params = {
headers: {
"Content-Type": "application/json",
},
};
try {
const response = http.get(url, params);
return response;
} catch (error) {
console.error(`Error getting docs: ${error.message}`);
throw error;
}
}

export function createSchema(bearerToken, schemaName, schemaVersion) {
const url = `${__ENV.CLOUDAPI_URL}/governance/v1/definitions/schemas`;
const params = {
Expand Down Expand Up @@ -836,6 +856,50 @@ export function getSchema(bearerToken, schemaName, schemaVersion) {
}
}

export function revokeCredential(issuerAccessToken, credentialExchangeId) {
const url = `${__ENV.CLOUDAPI_URL}/tenant/v1/issuer/credentials/revoke`;
const params = {
headers: {
"x-api-key": issuerAccessToken,
"Content-Type": "application/json",
},
};
try {
const requestBody = {
credential_exchange_id: credentialExchangeId,
auto_publish_on_ledger: true,
};
const response = http.post(url, JSON.stringify(requestBody), params);

if (response.status !== 200) {
console.error(`Unexpected status code: ${response.status}`);
console.error(`Response body: ${response.body}`);
}

return response;
} catch (error) {
console.error(`Error revoking credential: ${error.message}`);
throw error;
}
}

export function checkRevoked(issuerAccessToken, credentialExchangeId) {
const url = `${__ENV.CLOUDAPI_URL}/tenant/v1/issuer/credentials/revocation/record?credential_exchange_id=${credentialExchangeId}`;
const params = {
headers: {
"x-api-key": issuerAccessToken,
"Content-Type": "application/json",
},
};
try {
const response = http.get(url, params);
return response;
} catch (error) {
console.error(`Error checking if credential is revoked: ${error.message}`);
throw error;
}
}

// {
// "name": "load_pop",
// "version": "0.1.0",
Expand Down
40 changes: 29 additions & 11 deletions scripts/k6/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,43 @@
source ./env.sh

run_test() {
xk6 run -e SKIP_DELETE_ISSUERS="$1" -e SKIP_DELETE_HOLDERS="$2" "$3"
xk6 run "$1"
local exit_code=$?
if [ $exit_code -ne 0 ]; then
echo "Test $3 failed with exit code $exit_code"
echo "Deleting Holders"
xk6 run -e SKIP_DELETE_ISSUERS=true -e SKIP_DELETE_HOLDERS=false ./scenarios/delete-holders.js
xk6 run ./scenarios/delete-holders.js
if [ $MULTI_ISSUERS = false ]; then
export VUS=1 # delete single issuer
export ITERATIONS=1
fi
xk6 run ./scenarios/delete-issuers.js
echo "Exiting with exit code $exit_code ..."
exit $exit_code
fi
}

run_test true true ./scenarios/create-holders.js
run_test true true ./scenarios/create-invitation.js
run_test true true ./scenarios/create-credentials.js
run_test false true ./scenarios/create-proof.js
run_test true false ./scenarios/delete-holders.js
# Single issuer, multiple holder tests
export MULTI_ISSUERS=false
run_test ./scenarios/create-holders.js
run_test ./scenarios/create-invitation.js
run_test ./scenarios/create-credentials.js
run_test ./scenarios/create-proof.js
export ITERATIONS=$((ITERATIONS * VUS)) # revoke sequentially
export VUS=1
run_test ./scenarios/revoke-credentials.js
source ./env.sh # concurrent
export IS_REVOKED=true
run_test ./scenarios/create-proof.js

run_test false true ./scenarios/create-issuers.js
run_test ./scenarios/delete-holders.js
export VUS=1 # delete single issuer - TODO: improve this
export ITERATIONS=1
run_test ./scenarios/delete-issuers.js

echo "All tests completed successfully"

# TODO: Better logic to handle deleting issuers and holders
# Multiple issuers tests
source ./env.sh # concurrent
export MULTI_ISSUERS=true
run_test ./scenarios/create-issuers.js
run_test ./scenarios/create-creddef.js
run_test ./scenarios/delete-issuers.js
57 changes: 11 additions & 46 deletions scripts/k6/scenarios/create-credentials.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { check, sleep } from "k6";
import { SharedArray } from "k6/data";
import { Counter, Trend } from "k6/metrics";
import file from "k6/x/file";
import { getBearerToken } from "../libs/auth.js";
import {
acceptCredential,
Expand Down Expand Up @@ -51,6 +52,7 @@ export const options = {

const inputFilepath = "../output/create-invitation.json";
const data = open(inputFilepath, "r");
const outputFilepath = "output/create-credentials.json";

// const specificFunctionReqs = new Counter('specific_function_reqs');
const testFunctionReqs = new Counter("test_function_reqs");
Expand All @@ -75,6 +77,7 @@ export function setup() {
const bearerToken = getBearerToken();
const issuers = [];

file.writeString(outputFilepath, "");
const holders = data.trim().split("\n").map(JSON.parse);

// // Example usage of the loaded data
Expand Down Expand Up @@ -201,7 +204,9 @@ export default function (data) {
},
});

const { thread_id: threadId } = JSON.parse(createCredentialResponse.body);
const { thread_id: threadId, credential_exchange_id: credentialExchangeId } = JSON.parse(
createCredentialResponse.body,
);

// console.log(`Thread ID: ${threadId}`);
// console.log(`Holer access token: ${wallet.holder_access_token}`);
Expand Down Expand Up @@ -229,6 +234,11 @@ export default function (data) {
},
});

const issuerData = JSON.stringify({
credential_exchange_id: credentialExchangeId,
});
file.appendString(outputFilepath, `${issuerData}\n`);

// specificFunctionReqs.add(1, { my_custom_tag: 'specific_function' });

// const end = Date.now();
Expand All @@ -238,48 +248,3 @@ export default function (data) {
// sleep(1);
testFunctionReqs.add(1);
}

export function teardown(data) {
const bearerToken = data.bearerToken;
const issuers = data.issuers;
const wallets = data.holders;

// console.log(__ENV.SKIP_DELETE_ISSUERS)

if (__ENV.SKIP_DELETE_ISSUERS !== "true") {
for (const issuer of issuers) {
const deleteIssuerResponse = deleteTenant(bearerToken, issuer.walletId);
check(deleteIssuerResponse, {
"Delete Issuer Tenant Response status code is 200": (r) => {
if (r.status !== 200) {
console.error(`Unexpected response status while deleting issuer tenant ${issuer.walletId}: ${r.status}`);
return false;
}
console.log(`Deleted issuer tenant ${issuer.walletId} successfully.`);
return true;
},
});
}
} else {
console.log("Skipping deletion of issuer tenants.");
}
// // Delete holder tenants
if (__ENV.SKIP_DELETE_HOLDERS !== "true") {
for (const wallet of wallets) {
const walletId = getWalletIdByWalletName(bearerToken, wallet.wallet_name);
const deleteHolderResponse = deleteTenant(bearerToken, walletId);
check(deleteHolderResponse, {
"Delete Holder Tenant Response status code is 200": (r) => {
if (r.status !== 200) {
console.error(`Unexpected response status while deleting holder tenant ${walletId}: ${r.status}`);
return false;
}
console.log(`Deleted holder tenant ${walletId} successfully.`);
return true;
},
});
}
} else {
console.log("Skipping deletion of holder tenants.");
}
}
Loading
Loading