diff --git a/packages/cactus-plugin-ledger-connector-aries/package.json b/packages/cactus-plugin-ledger-connector-aries/package.json index 4058614cb3d..8e599cb6dc7 100644 --- a/packages/cactus-plugin-ledger-connector-aries/package.json +++ b/packages/cactus-plugin-ledger-connector-aries/package.json @@ -1,7 +1,7 @@ { "name": "@hyperledger/cactus-plugin-ledger-connector-aries", "version": "2.0.0-alpha.2", - "description": "Allows Cactus nodes to connect to an Anoncreds ledger and Aries agents.", + "description": "Allows Cactus nodes to connect to an Indy ledger and Aries agents.", "keywords": [ "Hyperledger", "Cacti", @@ -59,7 +59,6 @@ "@aries-framework/anoncreds-rs": "0.5.0-alpha.58", "@aries-framework/askar": "0.5.0-alpha.58", "@aries-framework/core": "0.5.0-alpha.58", - "@aries-framework/indy-sdk": "0.5.0-alpha.58", "@aries-framework/indy-vdr": "0.5.0-alpha.58", "@aries-framework/node": "0.5.0-alpha.58", "@hyperledger/anoncreds-nodejs": "0.2.0-dev.4", @@ -68,46 +67,29 @@ "@hyperledger/cactus-core": "2.0.0-alpha.2", "@hyperledger/cactus-core-api": "2.0.0-alpha.2", "@hyperledger/indy-vdr-nodejs": "0.2.0-dev.3", - "axios": "1.5.1", - "express": "4.18.2", - "http-proxy-middleware": "2.0.6", - "minimist": "1.2.8", - "prom-client": "13.2.0", - "run-time-error": "1.4.0", "rxjs": "7.8.1", - "sanitize-html": "2.7.0", - "socket.io-client": "4.5.4", - "typescript-optional": "2.0.1", - "web3": "4.1.2", - "web3-eth": "4.2.0", - "web3-eth-contract": "4.1.0" + "socket.io-client": "4.5.4" }, "devDependencies": { - "@hyperledger/cactus-plugin-keychain-memory": "2.0.0-alpha.2", - "@hyperledger/cactus-test-geth-ledger": "2.0.0-alpha.2", "@hyperledger/cactus-test-tooling": "2.0.0-alpha.2", "@types/body-parser": "1.19.4", "@types/express": "4.17.19", - "@types/js-yaml": "4.0.5", - "@types/minimist": "1.2.2", - "@types/sanitize-html": "2.6.2", "@types/uuid": "9.0.6", "body-parser": "1.20.2", - "chalk": "4.1.2", - "js-yaml": "4.1.0", + "express": "4.18.2", + "jest": "29.6.2", + "jest-extended": "4.0.1", "socket.io": "4.5.4", - "uuid": "9.0.1", - "web3-eth-accounts": "4.0.6" + "uuid": "9.0.1" }, "engines": { - "node": ">=10", - "npm": ">=6" + "node": ">=18", + "npm": ">=8" }, "publishConfig": { "access": "public" }, "browserMinified": "dist/cactus-plugin-ledger-connector-aries.web.umd.min.js", - "mainMinified": "dist/cactus-plugin-ledger-connector-aries.node.umd.min.js", "watch": { "codegen:openapi": { "patterns": [ diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/json/openapi.json b/packages/cactus-plugin-ledger-connector-aries/src/main/json/openapi.json index a68e1aa1711..45d2cf1e27a 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/main/json/openapi.json +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/json/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.3", "info": { "title": "Hyperledger Cacti Plugin - Connector Aries", - "description": "Can communicate with other Aries agents", + "description": "Can communicate with other Aries agents and Cacti Aries connectors", "version": "v2.0.0-alpha.2", "license": { "name": "Apache-2.0", @@ -58,7 +58,7 @@ }, "WatchProofStateV1": { "type": "string", - "description": "Websocket requests for monitoring connection change events.", + "description": "Websocket requests for monitoring proof state change events.", "enum": [ "org.hyperledger.cactus.api.async.hlaries.WatchProofStateV1.Subscribe", "org.hyperledger.cactus.api.async.hlaries.WatchProofStateV1.Next", @@ -76,7 +76,7 @@ }, "WatchProofStateOptionsV1": { "type": "object", - "description": "Options passed when monitoring proof change events.", + "description": "Options passed when monitoring proof state change events.", "required": ["agentName"], "properties": { "agentName": { @@ -604,7 +604,6 @@ } } }, - "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/create-new-connection-invitation": { "post": { "x-hyperledger-cacti": { diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/api-client/aries-api-client.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/api-client/aries-api-client.ts index f8bf902e562..9b21a8a92a8 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/api-client/aries-api-client.ts +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/api-client/aries-api-client.ts @@ -1,9 +1,16 @@ -import { Observable, ReplaySubject, Subscription } from "rxjs"; +import { Observable, ReplaySubject } from "rxjs"; import { finalize } from "rxjs/operators"; import { io } from "socket.io-client"; -import { Logger, Checks } from "@hyperledger/cactus-common"; -import { LogLevelDesc, LoggerProvider } from "@hyperledger/cactus-common"; +import { ProofState } from "@aries-framework/core"; + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, +} from "@hyperledger/cactus-common"; import { Constants } from "@hyperledger/cactus-core-api"; + import { AgentConnectionRecordV1, AriesProofExchangeRecordV1, @@ -17,7 +24,6 @@ import { WatchProofStateV1, } from "../generated/openapi/typescript-axios"; import { Configuration } from "../generated/openapi/typescript-axios/configuration"; -import { ProofExchangeRecord, ProofState } from "@aries-framework/core"; export class AriesApiClientOptions extends Configuration { readonly logLevel?: LogLevelDesc; @@ -25,8 +31,15 @@ export class AriesApiClientOptions extends Configuration { readonly wsApiPath?: string; } +/** + * Default timeout when waiting for certain operations (accepting proof, establishing connection, etc...) + */ +const DEFAULT_ARIES_OPERATION_TIMEOUT = 60 * 1000; + +/** + * How often to poll endpoints when waiting for operation to finish. + */ const WAIT_FOR_CONNECTION_READY_POLL_INTERVAL = 500; -const DEFAULT_ARIES_OPERATION_TIMEOUT = 60 * 1000; // must be greater than 1000 export class AriesApiClient extends DefaultApi { private readonly log: Logger; @@ -54,6 +67,13 @@ export class AriesApiClient extends DefaultApi { this.log.debug(`basePath=${this.options.basePath}`); } + /** + * Watch for connection state changes on given agent. + * Example: New connection request, connection accepted. + * + * @param options Monitor options + * @returns rxjs `Observable` + */ public watchConnectionStateV1( options: WatchConnectionStateOptionsV1, ): Observable { @@ -96,6 +116,13 @@ export class AriesApiClient extends DefaultApi { ); } + /** + * Watch for proof state changes on given agent. + * Example: Proof request received, proof request was accepted by peer + * + * @param options Monitor options + * @returns rxjs `Observable` + */ public watchProofStateV1( options: WatchProofStateOptionsV1, ): Observable { @@ -135,11 +162,22 @@ export class AriesApiClient extends DefaultApi { ); } + /** + * Helper method to wait for connection record to be created. + * This does not mean that connection is ready though, just that it was accepted by the peer. + * Will block until operation is completed. + * + * @param agentName agent that requested the connection. + * @param outOfBandId new connection outOfBandId + * @param timeout how much time to wait before giving up. + */ private async waitForConnectionRecordV1( agentName: string, outOfBandId: string, timeout = DEFAULT_ARIES_OPERATION_TIMEOUT, ): Promise { + Checks.truthy(agentName, "waitForConnectionRecordV1 arg agentName"); + Checks.truthy(outOfBandId, "waitForConnectionRecordV1 arg outOfBandId"); this.log.debug( "waitForConnectionRecordV1 for agent", agentName, @@ -205,7 +243,14 @@ export class AriesApiClient extends DefaultApi { }); } - async waitForConnectionReadyV1( + /** + * Wait (block execution) until connection with specified `outOfBandId` is ready (connected). + * + * @param agentName agent with specific connection + * @param outOfBandId connection outOfBandId + * @param timeout how much time to wait before giving up. Must be at least 1 second. + */ + public async waitForConnectionReadyV1( agentName: string, outOfBandId: string, timeout = DEFAULT_ARIES_OPERATION_TIMEOUT, @@ -252,7 +297,15 @@ export class AriesApiClient extends DefaultApi { ); } - async waitForInvitedPeerConnectionV1( + /** + * Wait until invitation URL is accepted by remote client and connection is established. + * Will block until operation is completed. + * + * @param agentName agent that requested the connection (sent the invitation) + * @param outOfBandId new connection outOfBandId + * @param timeout how much time to wait before giving up. + */ + public async waitForInvitedPeerConnectionV1( agentName: string, outOfBandId: string, timeout = DEFAULT_ARIES_OPERATION_TIMEOUT, @@ -273,11 +326,22 @@ export class AriesApiClient extends DefaultApi { await this.waitForConnectionReadyV1(agentName, outOfBandId, timeout); } - async waitForProofCompletionV1( + /** + * Wait until proof with specified ID was either accepted or denied. + * Will block until operation is completed. + * + * @param agentName agent that requested the proof. + * @param proofId id of the proof request. + * @param timeout how much time to wait before giving up. + */ + public async waitForProofCompletionV1( agentName: string, proofId: string, timeout = DEFAULT_ARIES_OPERATION_TIMEOUT, ): Promise { + Checks.truthy(agentName, "waitForProofCompletionV1 arg agentName"); + Checks.truthy(proofId, "waitForProofCompletionV1 arg proofId"); + return new Promise((resolve, reject) => { // Common cleanup method for leaving the method const cleanup = () => { @@ -331,12 +395,25 @@ export class AriesApiClient extends DefaultApi { }); } - async requestProofAndWaitV1( + /** + * Send proof request and wait for response (accepted / denied by a peer) + * Will block until operation is completed. + * + * @param agentName agent that will request the proof. + * @param connectionId peer connection id that should provide the proof. + * @param proofAttributes proof attributes to validate. + * @param timeout how much time to wait before giving up. + */ + public async requestProofAndWaitV1( agentName: string, connectionId: string, proofAttributes: CactiProofRequestAttributeV1[], timeout = DEFAULT_ARIES_OPERATION_TIMEOUT, ): Promise { + Checks.truthy(agentName, "requestProofAndWaitV1 arg agentName"); + Checks.truthy(connectionId, "requestProofAndWaitV1 arg connectionId"); + Checks.truthy(proofAttributes, "requestProofAndWaitV1 arg proofAttributes"); + const proof = await this.requestProofV1({ agentName, connectionId, diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/aries-types.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/aries-types.ts index f14e23a42e6..ec2681c1069 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/aries-types.ts +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/aries-types.ts @@ -1,13 +1,11 @@ -/////// -import { AskarModule } from "@aries-framework/askar"; +/** + * Helper functions used mostly to convert from Open API endpoint inputs to Aries compatible structures. + */ + import { Agent, - InitConfig, - HttpOutboundTransport, ConnectionsModule, DidsModule, - TypedArrayEncoder, - KeyType, CredentialsModule, V2CredentialProtocol, ProofsModule, @@ -19,34 +17,25 @@ import { DidExchangeState, DidExchangeRole, } from "@aries-framework/core"; -import { agentDependencies, HttpInboundTransport } from "@aries-framework/node"; -import { ariesAskar } from "@hyperledger/aries-askar-nodejs"; -import { - IndyVdrAnonCredsRegistry, - IndyVdrIndyDidRegistrar, - IndyVdrIndyDidResolver, - IndyVdrModule, - IndyVdrPoolConfig, -} from "@aries-framework/indy-vdr"; -import { indyVdr } from "@hyperledger/indy-vdr-nodejs"; -import { +import type { AskarModule } from "@aries-framework/askar"; +import type { IndyVdrModule } from "@aries-framework/indy-vdr"; +import type { AnonCredsCredentialFormatService, AnonCredsModule, AnonCredsProofFormatService, AnonCredsRequestedAttribute, } from "@aries-framework/anoncreds"; -import { AnonCredsRsModule } from "@aries-framework/anoncreds-rs"; -import { anoncreds } from "@hyperledger/anoncreds-nodejs"; +import type { AnonCredsRsModule } from "@aries-framework/anoncreds-rs"; + import { AgentConnectionsFilterV1, CactiAcceptPolicyV1, CactiProofRequestAttributeV1, } from "./public-api"; -/////////// /** * Aries JS Agent with Anoncreds/Indy/Askar modules configured. - * This is exact Agent type returned by factories used in this module. + * This is exact Agent type returned by factories used by this connector for now. */ export type AnoncredAgent = Agent<{ readonly connections: ConnectionsModule; @@ -63,6 +52,12 @@ export type AnoncredAgent = Agent<{ readonly askar: AskarModule; }>; +/** + * Convert Cacti OpenAPI input to Aries compatible `AutoAcceptProof` + * + * @param policy `CactiAcceptPolicyV1` + * @returns `AutoAcceptProof` + */ export function cactiAcceptPolicyToAutoAcceptProof( policy: CactiAcceptPolicyV1, ): AutoAcceptProof { @@ -79,6 +74,12 @@ export function cactiAcceptPolicyToAutoAcceptProof( } } +/** + * Convert Cacti OpenAPI input to Aries compatible `AutoAcceptCredential` + * + * @param policy `CactiAcceptPolicyV1` + * @returns `AutoAcceptCredential` + */ export function cactiAcceptPolicyToAutoAcceptCredential( policy: CactiAcceptPolicyV1, ): AutoAcceptCredential { @@ -95,22 +96,13 @@ export function cactiAcceptPolicyToAutoAcceptCredential( } } -export function safeStringToEnum>( - enumType: T, - value?: string, -): T[keyof T] | undefined { - if (!value) { - return undefined; - } - - const enumValue = (enumType as any)[value]; - if (enumValue === undefined) { - throw new Error(`Unknown ${enumType.name}: ${value}`); - } - - return enumValue; -} - +/** + * Validate and convert value to any enum (intended to use with AFJ but should work with any enum) + * + * @param enumType enum to validate the value against + * @param value string value + * @returns same value converted to the enum or undefined if value missing. Throws exception if validation failed. + */ export function validateEnumValue>( enumType: T, value?: string, @@ -119,21 +111,6 @@ export function validateEnumValue>( return undefined; } - if (!(value in enumType)) { - throw new Error(`Invalid enum value: ${value}`); - } - - return value as unknown as T[keyof T]; -} - -export function validateEnumValue2>( - enumType: T, - value?: string, -): T[keyof T] | undefined { - if (!value) { - return undefined; - } - if (!Object.values(enumType).includes(value)) { throw new Error(`Invalid aries enum value: ${value}`); } @@ -141,16 +118,29 @@ export function validateEnumValue2>( return value as unknown as T[keyof T]; } +/** + * Convert Cacti OpenAPI input to Aries compatible `ConnectionRecord` filter `Query`. + * Validates enums and throws exception if invalid value was used. + * + * @param filter `AgentConnectionsFilterV1` + * @returns `Query` + */ export function cactiAgentConnectionsFilterToQuery( filter: AgentConnectionsFilterV1, ): Query { return { ...filter, - state: validateEnumValue2(DidExchangeState, filter.state), - role: validateEnumValue2(DidExchangeRole, filter.role), + state: validateEnumValue(DidExchangeState, filter.state), + role: validateEnumValue(DidExchangeRole, filter.role), }; } +/** + * Convert Cacti OpenAPI input to Aries compatible proof request restrictions. + * + * @param proofAttributes `CactiProofRequestAttributeV1[]` + * @returns `Record` + */ export async function cactiAttributesToAnonCredsRequestedAttributes( proofAttributes: CactiProofRequestAttributeV1[], ): Promise> { diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/api.ts index 08e41d6ebeb..be74aaa6c52 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -2,7 +2,7 @@ /* eslint-disable */ /** * Hyperledger Cacti Plugin - Connector Aries - * Can communicate with other Aries agents + * Can communicate with other Aries agents and Cacti Aries connectors * * The version of the OpenAPI document: v2.0.0-alpha.2 * @@ -565,7 +565,7 @@ export type WatchConnectionStateV1 = typeof WatchConnectionStateV1[keyof typeof /** - * Options passed when monitoring proof change events. + * Options passed when monitoring proof state change events. * @export * @interface WatchProofStateOptionsV1 */ @@ -597,7 +597,7 @@ export interface WatchProofStateProgressV1 { 'previousState': string | null; } /** - * Websocket requests for monitoring connection change events. + * Websocket requests for monitoring proof state change events. * @export * @enum {string} */ diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/base.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/base.ts index c78e0f2c21f..1301b32d8ff 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/base.ts +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/base.ts @@ -2,7 +2,7 @@ /* eslint-disable */ /** * Hyperledger Cacti Plugin - Connector Aries - * Can communicate with other Aries agents + * Can communicate with other Aries agents and Cacti Aries connectors * * The version of the OpenAPI document: v2.0.0-alpha.2 * diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/common.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/common.ts index 74ef7295eaf..42c73a1d678 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/common.ts +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/common.ts @@ -2,7 +2,7 @@ /* eslint-disable */ /** * Hyperledger Cacti Plugin - Connector Aries - * Can communicate with other Aries agents + * Can communicate with other Aries agents and Cacti Aries connectors * * The version of the OpenAPI document: v2.0.0-alpha.2 * diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/configuration.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/configuration.ts index 8341419ae89..21a36f4aa5b 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/configuration.ts +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/configuration.ts @@ -2,7 +2,7 @@ /* eslint-disable */ /** * Hyperledger Cacti Plugin - Connector Aries - * Can communicate with other Aries agents + * Can communicate with other Aries agents and Cacti Aries connectors * * The version of the OpenAPI document: v2.0.0-alpha.2 * diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/index.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/index.ts index 5afdbda31d2..c6f463742db 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/index.ts +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/index.ts @@ -2,7 +2,7 @@ /* eslint-disable */ /** * Hyperledger Cacti Plugin - Connector Aries - * Can communicate with other Aries agents + * Can communicate with other Aries agents and Cacti Aries connectors * * The version of the OpenAPI document: v2.0.0-alpha.2 * diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/plugin-ledger-connector-aries.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/plugin-ledger-connector-aries.ts index d97f7f5a397..79cbcca74ee 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/plugin-ledger-connector-aries.ts +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/plugin-ledger-connector-aries.ts @@ -2,53 +2,7 @@ import type { Server as SocketIoServer, Socket as SocketIoSocket, } from "socket.io"; - -import { Express } from "express"; - -import OAS from "../json/openapi.json"; - -import { - IWebServiceEndpoint, - IPluginWebService, - ICactusPlugin, - ICactusPluginOptions, -} from "@hyperledger/cactus-core-api"; - -import { PluginRegistry } from "@hyperledger/cactus-core"; - -import { - Checks, - Logger, - LoggerProvider, - LogLevelDesc, - safeStringifyException, -} from "@hyperledger/cactus-common"; - -import { - AcceptInvitationV1Response, - AgentConnectionRecordV1, - AgentConnectionsFilterV1, - AriesAgentConfigV1, - AriesAgentSummaryV1, - AriesProofExchangeRecordV1, - CactiAcceptPolicyV1, - CactiProofRequestAttributeV1, - CreateNewConnectionInvitationV1Response, - WatchConnectionStateOptionsV1, - WatchConnectionStateV1, - WatchProofStateOptionsV1, - WatchProofStateV1, -} from "./generated/openapi/typescript-axios"; - -import { WatchConnectionStateV1Endpoint } from "./web-services/watch-connection-state-v1-endpoint"; -import { WatchProofStateV1Endpoint } from "./web-services/watch-proof-state-v1-endpoint"; -import { GetAgentsEndpoint } from "./web-services/get-agents-v1-endpoint"; -import { RquestProofEndpoint } from "./web-services/request-proof-v1-endpoint"; -import { GetConnectionsEndpoint } from "./web-services/get-connections-v1-endpoint"; -import { CreateNewConnectionInvitationEndpoint } from "./web-services/create-new-connection-invitation-v1-endpoint"; -import { AcceptInvitationEndpoint } from "./web-services/accept-invitation-v1-endpoint"; - -/////// +import type { Express } from "express"; import { AskarModule } from "@aries-framework/askar"; import { Agent, @@ -64,7 +18,6 @@ import { AutoAcceptProof, V2ProofProtocol, AutoAcceptCredential, - OutOfBandRecord, } from "@aries-framework/core"; import { agentDependencies, HttpInboundTransport } from "@aries-framework/node"; import { ariesAskar } from "@hyperledger/aries-askar-nodejs"; @@ -83,16 +36,53 @@ import { } from "@aries-framework/anoncreds"; import { AnonCredsRsModule } from "@aries-framework/anoncreds-rs"; import { anoncreds } from "@hyperledger/anoncreds-nodejs"; -/////////// +import { + IWebServiceEndpoint, + IPluginWebService, + ICactusPlugin, + ICactusPluginOptions, +} from "@hyperledger/cactus-core-api"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { + Checks, + Logger, + LoggerProvider, + LogLevelDesc, + safeStringifyException, +} from "@hyperledger/cactus-common"; + +import OAS from "../json/openapi.json"; +import { + AcceptInvitationV1Response, + AgentConnectionRecordV1, + AgentConnectionsFilterV1, + AriesAgentConfigV1, + AriesAgentSummaryV1, + AriesProofExchangeRecordV1, + CactiProofRequestAttributeV1, + CreateNewConnectionInvitationV1Response, + WatchConnectionStateOptionsV1, + WatchConnectionStateV1, + WatchProofStateOptionsV1, + WatchProofStateV1, +} from "./generated/openapi/typescript-axios"; import { AnoncredAgent, - cactiAcceptPolicyToAutoAcceptCredential, - cactiAcceptPolicyToAutoAcceptProof, cactiAgentConnectionsFilterToQuery, cactiAttributesToAnonCredsRequestedAttributes, } from "./aries-types"; +import { WatchConnectionStateV1Endpoint } from "./web-services/watch-connection-state-v1-endpoint"; +import { WatchProofStateV1Endpoint } from "./web-services/watch-proof-state-v1-endpoint"; +import { GetAgentsEndpoint } from "./web-services/get-agents-v1-endpoint"; +import { RquestProofEndpoint } from "./web-services/request-proof-v1-endpoint"; +import { GetConnectionsEndpoint } from "./web-services/get-connections-v1-endpoint"; +import { CreateNewConnectionInvitationEndpoint } from "./web-services/create-new-connection-invitation-v1-endpoint"; +import { AcceptInvitationEndpoint } from "./web-services/accept-invitation-v1-endpoint"; + +const DEFAULT_INVITATION_DOMAIN = "https://example.org"; + export interface IPluginLedgerConnectorAriesOptions extends ICactusPluginOptions { logLevel?: LogLevelDesc; @@ -101,8 +91,6 @@ export interface IPluginLedgerConnectorAriesOptions invitationDomain?: string; } -const DEFAULT_INVITATION_DOMAIN = "https://example.org"; - export class PluginLedgerConnectorAries implements ICactusPlugin, IPluginWebService { @@ -144,6 +132,9 @@ export class PluginLedgerConnectorAries return this.instanceId; } + /** + * Closes all socketio connections and shutdowns every agent configured in this connector. + */ public async shutdown(): Promise { this.log.info(`Shutting down ${this.className}...`); @@ -158,15 +149,16 @@ export class PluginLedgerConnectorAries this.ariesAgents.clear(); } - public async onPluginInit(): Promise { + /** + * Creates Aries agent instances defined in connector constructor. + */ + public async onPluginInit(): Promise { if (this.ariesAgentConfigs) { this.log.info("Create aries agent instances"); for (const agentConfig of this.ariesAgentConfigs) { await this.addAriesAgent(agentConfig); } } - - return; } async registerWebServices( @@ -174,9 +166,12 @@ export class PluginLedgerConnectorAries wsApi: SocketIoServer, ): Promise { const { logLevel } = this.options; + + // Register OpenAPI const webServices = await this.getOrCreateWebServices(); await Promise.all(webServices.map((ws) => ws.registerExpress(app))); + // Register SocketIO (on new connection from the client) wsApi.on("connection", (socket: SocketIoSocket) => { this.log.info(`New Socket connected. ID=${socket.id}`); this.connectedSockets.set(socket.id, socket); @@ -227,7 +222,6 @@ export class PluginLedgerConnectorAries this.connectedSockets.delete(socket.id); }); }); - this.log.info(`WebSocketProvider created for socketio endpoints`); return webServices; } @@ -282,12 +276,17 @@ export class PluginLedgerConnectorAries return `@hyperledger/cactus-plugin-ledger-connector-aries`; } + /** + * Get summary of Aries agents managed by this connector. + * + * @returns Summary of Aries agents. + */ public async getAgents(): Promise { const allAgents = new Array(...this.ariesAgents.values()); return allAgents.map((agent) => { if (!agent.config.walletConfig) { - throw new Error( - `Agent ${agent.config.label} doesn't have wallet configured`, + this.log.error( + `Agent ${agent.config.label} doesn't have a wallet configured!`, ); } @@ -297,20 +296,43 @@ export class PluginLedgerConnectorAries isWalletInitialized: agent.wallet.isInitialized, isWalletProvisioned: agent.wallet.isProvisioned, walletConfig: { - id: agent.config.walletConfig.id, - type: agent.config.walletConfig.storage?.type ?? "unknown", + id: agent.config?.walletConfig?.id ?? "unknown", + type: agent.config?.walletConfig?.storage?.type ?? "unknown", }, endpoints: agent.config.endpoints, }; }); } + /** + * Get Aries agent with provided name from this connector or throw error. + * @param agentName agent name to get. + * + * @returns `AnoncredAgent` + */ + public async getAriesAgentOrThrow(agentName: string): Promise { + const agent = this.ariesAgents.get(agentName); + if (!agent) { + throw new Error(`No agent with a name ${agentName}`); + } + return agent; + } + + /** + * Get Aries agent modules that matches current default configuration for this connector. + * Right now only Anoncreds on Indy is supported. + * + * @param agentConfig Agent configuration. + * @returns Modules that can be used to create new Aries agent. + */ private getAskarAnonCredsIndyModules(agentConfig: AriesAgentConfigV1) { if (!agentConfig.indyNetworks || agentConfig.indyNetworks.length === 0) { throw new Error( `Agent ${agentConfig.name} must have at least one Indy network defined!`, ); } + + // For now we assume default accept policies but in the future we can use the user input: // const autoAcceptConnections = agentConfig.autoAcceptConnections ?? false; // this.log.debug( // `Agent ${agentConfig.name} autoAcceptConnections:`, @@ -397,11 +419,23 @@ export class PluginLedgerConnectorAries } as const; } + /** + * Create and add new Aries agent to this connector. + * Agent can later be used to interact with other Aries agent and Indy VDR. + * Wallet ID and agent label will be the same as provided agent name. + * + * @param agentConfig Agent configuration. + * @returns newly created Aries agent. + */ public async addAriesAgent( agentConfig: AriesAgentConfigV1, ): Promise { Checks.truthy(agentConfig, `addAriesAgent arg agentConfig`); Checks.truthy(agentConfig.name, `addAriesAgent arg agentConfig.name`); + Checks.truthy( + !this.ariesAgents.has(agentConfig.name), + `addAriesAgent arg agentConfig.name already used`, + ); Checks.truthy( agentConfig.walletKey, `addAriesAgent arg agentConfig.walletKey`, @@ -439,12 +473,20 @@ export class PluginLedgerConnectorAries await agent.initialize(); this.ariesAgents.set(agentConfig.name, agent); + this.log.info("addAriesAgent(): New agent", agentConfig.name); return agent; } + /** + * Remove Aries agent with provided name from this connector. + * + * @param agentName agent name to remove + */ public async removeAriesAgent(agentName: string): Promise { + Checks.truthy(agentName, `removeAriesAgent arg agentName`); const agent = this.ariesAgents.get(agentName); + if (agent) { await agent.shutdown(); this.ariesAgents.delete(agentName); @@ -457,20 +499,28 @@ export class PluginLedgerConnectorAries } } - // todo - private - async getAriesAgentOrThrow(agentName: string): Promise { - const agent = this.ariesAgents.get(agentName); - if (!agent) { - throw new Error(`No agent with a name ${agentName}`); - } - return agent; - } - - async importExistingIndyDidFromPrivateKey( + /** + * Import existing DID using private key. + * + * @param agentName Name of an agent that should own the DID. + * @param seed private seed to recreate DID. + * @param indyNamespace VDR namespace. + * @returns newly created DID string. + */ + public async importExistingIndyDidFromPrivateKey( agentName: string, seed: string, indyNamespace: string, ): Promise { + Checks.truthy( + agentName, + `importExistingIndyDidFromPrivateKey arg agentName`, + ); + Checks.truthy(seed, `importExistingIndyDidFromPrivateKey arg seed`); + Checks.truthy( + indyNamespace, + `importExistingIndyDidFromPrivateKey arg indyNamespace`, + ); const agent = await this.getAriesAgentOrThrow(agentName); const seedBuffer = TypedArrayEncoder.fromString(seed); @@ -493,10 +543,43 @@ export class PluginLedgerConnectorAries return did; } - async createNewConnectionInvitation( + /** + * Get connections established by an agent (both completed and in progress). + * List can be filtered. + * + * @param agentName get connections for specific agent + * @param filter fields to filter connections by + * @returns list of matching connection records + */ + public async getConnections( + agentName: string, + filter: AgentConnectionsFilterV1 = {}, + ): Promise { + Checks.truthy(agentName, "getConnections agentName options"); + const agent = await this.getAriesAgentOrThrow(agentName); + const allRecords = await agent.connections.findAllByQuery( + cactiAgentConnectionsFilterToQuery(filter), + ); + return allRecords.map((c) => { + return { + ...c, + isReady: c.isReady, + }; + }); + } + + /** + * Create Aries agent invitation URL that other agents can use to connect to this one. + * + * @param agentName agent name that should create invitation. + * @param invitationDomain URL domain to use (will use connector default if not specified) + * @returns `invitationURL` and connection `outOfBandId` + */ + public async createNewConnectionInvitation( agentName: string, invitationDomain?: string, ): Promise { + Checks.truthy(agentName, `createNewConnectionInvitation arg agentName`); const agent = await this.getAriesAgentOrThrow(agentName); const outOfBandRecord = await agent.oob.createInvitation(); @@ -508,10 +591,19 @@ export class PluginLedgerConnectorAries }; } - async acceptInvitation( + /** + * Accept invitation from another agent using invitation URL. + * + * @param agentName agent name that should accept invitation. + * @param invitationUrl aries agent invitation URL. + * @returns established connection `outOfBandId` + */ + public async acceptInvitation( agentName: string, invitationUrl: string, ): Promise { + Checks.truthy(agentName, `acceptInvitation arg agentName`); + Checks.truthy(invitationUrl, `acceptInvitation arg invitationUrl`); const agent = await this.getAriesAgentOrThrow(agentName); const { outOfBandRecord } = @@ -522,36 +614,26 @@ export class PluginLedgerConnectorAries }; } - async getConnections( - agentName: string, - filter: AgentConnectionsFilterV1 = {}, - ): Promise { - Checks.truthy(agentName, "getConnections agentName options"); - const agent = await this.getAriesAgentOrThrow(agentName); - const allRecords = await agent.connections.findAllByQuery( - cactiAgentConnectionsFilterToQuery(filter), - ); - return allRecords.map((c) => { - return { - ...c, - isReady: c.isReady, - }; - }); - } - - async requestProof( + /** + * Request credential proof from connected agent. + * + * @param agentName agent name requesting the proof. + * @param connectionId peer agent connection ID that must provide the proof. + * @param proofAttributes credential attributes to verify. + * @returns proof record + */ + public async requestProof( agentName: string, connectionId: string, proofAttributes: CactiProofRequestAttributeV1[], ): Promise { - Checks.truthy(agentName, "getConnections agentName options"); - Checks.truthy(connectionId, "getConnections connectionId options"); - Checks.truthy(proofAttributes, "getConnections proofAttributes options"); + Checks.truthy(agentName, "requestProof agentName options"); + Checks.truthy(connectionId, "requestProof connectionId options"); + Checks.truthy(proofAttributes, "requestProof proofAttributes options"); Checks.truthy( proofAttributes.length > 0, - "getConnections proofAttributes must be at least one", + "requestProof proofAttributes - must be at least one", ); - const agent = await this.getAriesAgentOrThrow(agentName); const proof = await agent.proofs.requestProof({ diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/watch-connection-state-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/watch-connection-state-v1-endpoint.ts index db4dd1ad966..0cb0a718b68 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/watch-connection-state-v1-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/watch-connection-state-v1-endpoint.ts @@ -1,29 +1,21 @@ import { Socket as SocketIoSocket } from "socket.io"; +import { + ConnectionEventTypes, + ConnectionStateChangedEvent, +} from "@aries-framework/core"; import { Logger, LogLevelDesc, LoggerProvider, Checks, - safeStringifyException, } from "@hyperledger/cactus-common"; import { WatchConnectionStateProgressV1, WatchConnectionStateV1, } from "../generated/openapi/typescript-axios"; - -/////// -import { - Agent, - ConnectionEventTypes, - ConnectionStateChangedEvent, - DidExchangeState, - OutOfBandRecord, - ConnectionRecord, -} from "@aries-framework/core"; import { AnoncredAgent } from "../aries-types"; -/////////// export interface IWatchConnectionStateV1EndpointConfiguration { logLevel?: LogLevelDesc; diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/watch-proof-state-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/watch-proof-state-v1-endpoint.ts index 8a7d90af39d..d0f1a27f949 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/watch-proof-state-v1-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/watch-proof-state-v1-endpoint.ts @@ -1,31 +1,18 @@ import { Socket as SocketIoSocket } from "socket.io"; +import { ProofStateChangedEvent, ProofEventTypes } from "@aries-framework/core"; import { Logger, LogLevelDesc, LoggerProvider, Checks, - safeStringifyException, } from "@hyperledger/cactus-common"; import { - AriesProofExchangeRecordV1, WatchProofStateProgressV1, WatchProofStateV1, } from "../generated/openapi/typescript-axios"; - -/////// -import { - Agent, - ConnectionEventTypes, - ProofStateChangedEvent, - DidExchangeState, - OutOfBandRecord, - ConnectionRecord, - ProofEventTypes, -} from "@aries-framework/core"; import { AnoncredAgent } from "../aries-types"; -/////////// export interface IWatchProofStateV1EndpointConfiguration { logLevel?: LogLevelDesc; @@ -88,7 +75,7 @@ export class WatchProofStateV1Endpoint { }); log.debug( - `Subscribing to connection state changes on aries agent ${agent.config.label}...`, + `Subscribing to proof state changes on aries agent ${agent.config.label}...`, ); } } diff --git a/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/basic-aries-check.test.ts b/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/aries-setup-and-connections.test.ts similarity index 93% rename from packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/basic-aries-check.test.ts rename to packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/aries-setup-and-connections.test.ts index d8c17440ce7..1d5625935b9 100644 --- a/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/basic-aries-check.test.ts +++ b/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/aries-setup-and-connections.test.ts @@ -6,14 +6,14 @@ const containerImageName = "ghcr.io/outsh/cactus-indy-all-in-one"; const containerImageVersion = "0.1"; -// For development on local sawtooth network +// For development on local indy network // 1. leaveLedgerRunning = true, useRunningLedger = false to run ledger and leave it running after test finishes. // 2. leaveLedgerRunning = true, useRunningLedger = true to use that ledger in future runs. const leaveLedgerRunning = false; const useRunningLedger = false; // Log settings -const testLogLevel: LogLevelDesc = "debug"; +const testLogLevel: LogLevelDesc = "info"; import "jest-extended"; import express from "express"; @@ -46,25 +46,19 @@ import { AgentConnectionRecordV1, } from "../../../main/typescript/public-api"; -import { - Agent, - ConnectionEventTypes, - ConnectionStateChangedEvent, - DidExchangeState, - OutOfBandRecord, - ConnectionRecord, -} from "@aries-framework/core"; - // Logger setup const log: Logger = LoggerProvider.getOrCreate({ - label: "basic-indy-check.test", + label: "aries-setup-and-connections.test", level: testLogLevel, }); const AFJ_WALLET_PATH = path.join(os.homedir(), ".afj/data/wallet/"); -// TODO - spearate? -describe("Connector setup tests", () => { +////////////////////////////////// +// Setup Tests +////////////////////////////////// + +describe.only("Aries connector setup tests", () => { const fakeIndyNetworkConfig = { isProduction: false, genesisTransactions: `{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blskey":"4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba","blskey_pop":"RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1","client_ip":"172.16.0.2","client_port":9702,"node_ip":"172.16.0.2","node_port":9701,"services":["VALIDATOR"]},"dest":"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv"},"metadata":{"from":"Th7MpTaRZVRYnPiabds81Y"},"type":"0"},"txnMetadata":{"seqNo":1,"txnId":"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62"},"ver":"1"}`, @@ -88,15 +82,15 @@ describe("Connector setup tests", () => { await connector.shutdown(); } - if (agentName) { - try { - const walletPath = path.join(AFJ_WALLET_PATH, agentName); - await rmdir(walletPath, { recursive: true, maxRetries: 5 }); - log.info(`${walletPath} remove successfully.`); - } catch (error) { - log.warn(`${agentName} could not be removed:`, error); - } - } + // if (agentName) { + // try { + // const walletPath = path.join(AFJ_WALLET_PATH, agentName); + // await rmdir(walletPath, { recursive: true, maxRetries: 5 }); + // log.info(`${walletPath} remove successfully.`); + // } catch (error) { + // log.warn(`${agentName} could not be removed:`, error); + // } + // } }); test("Basic connector construction works", async () => { @@ -221,7 +215,11 @@ describe("Connector setup tests", () => { }); }); -describe("Schemas and credential creation", () => { +////////////////////////////////// +// Connect Tests +////////////////////////////////// + +describe("Connect Aries agents through connector tests", () => { let addressInfo, address: string, port: number, @@ -243,15 +241,10 @@ describe("Schemas and credential creation", () => { path: Constants.SocketIoConnectionPathV1, }); - ////////////////////////////////// - // Setup - ////////////////////////////////// - beforeAll(async () => { const pruning = pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); await expect(pruning).resolves.toBeTruthy(); - //ledger = new GethTestLedger({ emitContainerLogs: true, testLogLevel }); ledger = new IndyTestLedger({ containerImageName, containerImageVersion, diff --git a/packages/cactus-plugin-ledger-connector-aries/tsconfig.json b/packages/cactus-plugin-ledger-connector-aries/tsconfig.json index 81650c3f16f..67a17b3ca76 100644 --- a/packages/cactus-plugin-ledger-connector-aries/tsconfig.json +++ b/packages/cactus-plugin-ledger-connector-aries/tsconfig.json @@ -19,14 +19,8 @@ { "path": "../cactus-core-api/tsconfig.json" }, - { - "path": "../cactus-plugin-keychain-memory/tsconfig.json" - }, { "path": "../cactus-test-tooling/tsconfig.json" - }, - { - "path": "../cactus-test-geth-ledger/tsconfig.json" } ] } diff --git a/yarn.lock b/yarn.lock index e8f7e357c83..93ac8e02a2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7991,7 +7991,6 @@ __metadata: "@aries-framework/anoncreds-rs": 0.5.0-alpha.58 "@aries-framework/askar": 0.5.0-alpha.58 "@aries-framework/core": 0.5.0-alpha.58 - "@aries-framework/indy-sdk": 0.5.0-alpha.58 "@aries-framework/indy-vdr": 0.5.0-alpha.58 "@aries-framework/node": 0.5.0-alpha.58 "@hyperledger/anoncreds-nodejs": 0.2.0-dev.4 @@ -7999,35 +7998,19 @@ __metadata: "@hyperledger/cactus-common": 2.0.0-alpha.2 "@hyperledger/cactus-core": 2.0.0-alpha.2 "@hyperledger/cactus-core-api": 2.0.0-alpha.2 - "@hyperledger/cactus-plugin-keychain-memory": 2.0.0-alpha.2 - "@hyperledger/cactus-test-geth-ledger": 2.0.0-alpha.2 "@hyperledger/cactus-test-tooling": 2.0.0-alpha.2 "@hyperledger/indy-vdr-nodejs": 0.2.0-dev.3 "@types/body-parser": 1.19.4 "@types/express": 4.17.19 - "@types/js-yaml": 4.0.5 - "@types/minimist": 1.2.2 - "@types/sanitize-html": 2.6.2 "@types/uuid": 9.0.6 - axios: 1.5.1 body-parser: 1.20.2 - chalk: 4.1.2 express: 4.18.2 - http-proxy-middleware: 2.0.6 - js-yaml: 4.1.0 - minimist: 1.2.8 - prom-client: 13.2.0 - run-time-error: 1.4.0 + jest: 29.6.2 + jest-extended: 4.0.1 rxjs: 7.8.1 - sanitize-html: 2.7.0 socket.io: 4.5.4 socket.io-client: 4.5.4 - typescript-optional: 2.0.1 uuid: 9.0.1 - web3: 4.1.2 - web3-eth: 4.2.0 - web3-eth-accounts: 4.0.6 - web3-eth-contract: 4.1.0 languageName: unknown linkType: soft