diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8b262fc1d9..609c7be6a7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,6 +19,7 @@ jobs: compute_changed_packages: outputs: cmd-api-server-changed: ${{ steps.changes.outputs.cmd-api-server-changed }} + plugin-ledger-connector-aries-changed: ${{ steps.changes.outputs.plugin-ledger-connector-aries-changed }} plugin-ledger-connector-besu-changed: ${{ steps.changes.outputs.plugin-ledger-connector-besu-changed }} plugin-ledger-connector-corda-changed: ${{ steps.changes.outputs.plugin-ledger-connector-corda-changed }} plugin-ledger-connector-fabric-changed: ${{ steps.changes.outputs.plugin-ledger-connector-fabric-changed }} @@ -48,6 +49,14 @@ jobs: - './packages/cactus-plugin-keychain-vault/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' # - './.github/workflows/ci.yaml' + plugin-ledger-connector-aries-changed: + - './packages/cactus-plugin-ledger-connector-aries/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' + - './packages/cactus-common/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' + - './packages/cactus-core/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' + - './packages/cactus-core-api/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' + - './packages/cactus-test-tooling/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' + # - './.github/workflows/ci.yaml' + plugin-ledger-connector-besu-changed: - './packages/cactus-plugin-ledger-connector-besu/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' - './packages/cactus-test-plugin-ledger-connector-besu/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' @@ -57,7 +66,7 @@ jobs: - './packages/cactus-test-tooling/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' - './packages/cactus-plugin-keychain-memory/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' # - './.github/workflows/ci.yaml'' - + plugin-ledger-connector-corda-changed: - './packages/cactus-plugin-ledger-connector-corda/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' - './packages/cactus-common/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' @@ -84,7 +93,7 @@ jobs: - './packages/cactus-test-geth-ledger/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' - './packages/cactus-plugin-keychain-memory/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' # - './.github/workflows/ci.yaml' - + plugin-ledger-connector-iroha2-changed: - './packages/cactus-plugin-ledger-connector-iroha2/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' - './packages/cactus-common/**!(*.md|*.css|*.html|*.jpg|*.jpeg|*.png)' @@ -347,7 +356,7 @@ jobs: - run: ./tools/ci.sh cactus-cmd-api-server: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.cmd-api-server-changed == 'true' @@ -884,6 +893,33 @@ jobs: node-version: ${{ env.NODEJS_VERSION }} - uses: actions/checkout@v3.5.2 + - id: yarn-cache + name: Restore Yarn Cache + uses: actions/cache@v3.3.1 + with: + key: ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} + path: ./.yarn/ + restore-keys: | + ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} + - run: ./tools/ci.sh + cactus-plugin-ledger-connector-aries: + continue-on-error: false + needs: + - build-dev + - compute_changed_packages + if: needs.compute_changed_packages.outputs.plugin-ledger-connector-aries-changed == 'true' + env: + FULL_BUILD_DISABLED: true + JEST_TEST_PATTERN: packages/cactus-plugin-ledger-connector-aries/src/test/typescript/(unit|integration|benchmark)/.*/*.test.ts + JEST_TEST_RUNNER_DISABLED: false + TAPE_TEST_RUNNER_DISABLED: true + runs-on: ubuntu-20.04 + steps: + - name: Use Node.js ${{ env.NODEJS_VERSION }} + uses: actions/setup-node@v3.6.0 + with: + node-version: ${{ env.NODEJS_VERSION }} + - uses: actions/checkout@v3.5.2 - id: yarn-cache name: Restore Yarn Cache uses: actions/cache@v3.3.1 @@ -895,7 +931,7 @@ jobs: - run: ./tools/ci.sh cactus-plugin-ledger-connector-besu: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-besu-changed == 'true' @@ -925,7 +961,7 @@ jobs: - run: ./tools/ci.sh cactus-plugin-ledger-connector-corda: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-corda-changed == 'true' @@ -955,7 +991,7 @@ jobs: - run: ./tools/ci.sh plugin-ledger-connector-fabric-0: - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -988,7 +1024,7 @@ jobs: - run: ./tools/ci.sh plugin-ledger-connector-fabric-1: - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1022,7 +1058,7 @@ jobs: plugin-ledger-connector-fabric-2: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1054,7 +1090,7 @@ jobs: - run: yarn ts-node ./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-typescript-source.test.ts plugin-ledger-connector-fabric-3: - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1088,7 +1124,7 @@ jobs: plugin-ledger-connector-fabric-4: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1121,7 +1157,7 @@ jobs: plugin-ledger-connector-fabric-5: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1154,7 +1190,7 @@ jobs: plugin-ledger-connector-fabric-6: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1187,7 +1223,7 @@ jobs: plugin-ledger-connector-fabric-7: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1220,7 +1256,7 @@ jobs: plugin-ledger-connector-fabric-8: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1253,7 +1289,7 @@ jobs: plugin-ledger-connector-fabric-9: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1285,7 +1321,7 @@ jobs: - run: yarn ts-node ./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/obtain-profiles.test.ts plugin-ledger-connector-fabric-10: - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1319,7 +1355,7 @@ jobs: plugin-ledger-connector-fabric-11: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1352,7 +1388,7 @@ jobs: plugin-ledger-connector-fabric-12: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1438,7 +1474,7 @@ jobs: - run: ./tools/ci.sh cactus-plugin-ledger-connector-iroha2: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-iroha2-changed == 'true' @@ -1466,7 +1502,7 @@ jobs: - run: ./tools/ci.sh cactus-plugin-ledger-connector-ethereum: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-ethereum-changed == 'true' @@ -1493,7 +1529,7 @@ jobs: - run: ./tools/ci.sh cactus-plugin-ledger-connector-quorum: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-quorum-changed == 'true' @@ -1705,7 +1741,7 @@ jobs: - run: ./tools/ci.sh cactus-test-cmd-api-server: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.cmd-api-server-changed == 'true' @@ -1845,7 +1881,7 @@ jobs: - run: ./tools/ci.sh cactus-test-plugin-ledger-connector-besu: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -1875,7 +1911,7 @@ jobs: - run: ./tools/ci.sh cactus-test-plugin-ledger-connector-quorum: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-quorum-changed == 'true' @@ -1930,7 +1966,7 @@ jobs: - run: ./tools/ci.sh cactus-test-tooling: continue-on-error: false - needs: + needs: - build-dev - compute_changed_packages if: needs.compute_changed_packages.outputs.test-tooling-changed == 'true' @@ -2000,7 +2036,7 @@ jobs: severity: 'CRITICAL,HIGH' ghcr-cmd-api-server: runs-on: ubuntu-20.04 - needs: + needs: - compute_changed_packages if: needs.compute_changed_packages.outputs.cmd-api-server-changed == 'true' steps: @@ -2017,7 +2053,7 @@ jobs: vuln-type: 'os,library' severity: 'CRITICAL,HIGH' ghcr-connector-besu: - needs: + needs: - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-besu-changed == 'true' runs-on: ubuntu-20.04 @@ -2036,7 +2072,7 @@ jobs: severity: 'CRITICAL,HIGH' ghcr-connector-corda-server: runs-on: ubuntu-20.04 - needs: + needs: - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-corda-changed == 'true' @@ -2055,7 +2091,7 @@ jobs: severity: 'CRITICAL,HIGH' ghcr-connector-fabric: runs-on: ubuntu-20.04 - needs: + needs: - compute_changed_packages if: needs.compute_changed_packages.outputs.plugin-ledger-connector-fabric-changed == 'true' @@ -2074,7 +2110,7 @@ jobs: severity: 'CRITICAL,HIGH' ghcr-corda-all-in-one: runs-on: ubuntu-20.04 - needs: + needs: - compute_changed_packages if: needs.compute_changed_packages.outputs.ghcr-corda-all-in-one-changed == 'true' steps: @@ -2098,7 +2134,7 @@ jobs: run: DOCKER_BUILDKIT=1 docker build ./tools/docker/corda-all-in-one/corda-v4_8-flowdb/ ghcr-corda-all-in-one-obligation: runs-on: ubuntu-20.04 - needs: + needs: - compute_changed_packages if: needs.compute_changed_packages.outputs.ghcr-corda-all-in-one-obligation-changed == 'true' steps: @@ -2116,7 +2152,7 @@ jobs: severity: 'CRITICAL,HIGH' ghcr-dev-container-vscode: runs-on: ubuntu-20.04 - needs: + needs: - compute_changed_packages if: needs.compute_changed_packages.outputs.ghcr-dev-container-vscode-changed == 'true' env: diff --git a/examples/cactus-example-discounted-asset-trade-client/package.json b/examples/cactus-example-discounted-asset-trade-client/package.json index 040dc43f0f..7bf95bcd75 100644 --- a/examples/cactus-example-discounted-asset-trade-client/package.json +++ b/examples/cactus-example-discounted-asset-trade-client/package.json @@ -55,18 +55,18 @@ "@hyperledger/anoncreds-nodejs": "0.2.0-dev.4", "@hyperledger/aries-askar-nodejs": "0.2.0-dev.1", "@hyperledger/indy-vdr-nodejs": "0.2.0-dev.3", - "axios": "1.5.1", + "axios": "1.6.0", "inquirer": "8.2.6", "loglevel": "1.8.1" }, "devDependencies": { - "@aries-framework/anoncreds": "0.5.0-alpha.58", - "@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", + "@aries-framework/anoncreds": "0.5.0-alpha.71", + "@aries-framework/anoncreds-rs": "0.5.0-alpha.71", + "@aries-framework/askar": "0.5.0-alpha.71", + "@aries-framework/core": "0.5.0-alpha.71", + "@aries-framework/indy-sdk": "0.5.0-alpha.71", + "@aries-framework/indy-vdr": "0.5.0-alpha.71", + "@aries-framework/node": "0.5.0-alpha.71", "@types/inquirer": "8.2.6" }, "engines": { diff --git a/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/agent-setup.ts b/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/agent-setup.ts index 98b787b48e..cda75b37b5 100644 --- a/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/agent-setup.ts +++ b/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/agent-setup.ts @@ -3,6 +3,8 @@ */ import * as log from "loglevel"; +import * as path from "node:path"; +import * as os from "node:os"; import { readFileSync } from "fs"; import { AskarModule } from "@aries-framework/askar"; @@ -51,6 +53,11 @@ const ISSUER_AGENT_PORT = 3004; const ISSUER_DID_SEED = "000000000000000000000000Steward1"; const DID_INDY_NAMESPACE = "cacti:test"; +const WALLET_PATH = path.join( + os.homedir(), + ".cacti/cactus-example-discounted-asset-trade/wallet", +); + // Read Genesis transactions const genesisTransactionsPath = "/etc/cactus/indy-all-in-one/pool_transactions_genesis"; @@ -58,9 +65,9 @@ log.info( "Reading Indy genesis transactions from file:", genesisTransactionsPath, ); -const genesisTransactions = readFileSync( - "/etc/cactus/indy-all-in-one/pool_transactions_genesis", -).toString("utf-8"); +const genesisTransactions = readFileSync(genesisTransactionsPath).toString( + "utf-8", +); /** * Configuration for local indy-all-in-one ledger. @@ -142,11 +149,17 @@ export async function setupAgent( name: string, port: number, ): Promise { + const walletPath = path.join(WALLET_PATH, `${name}.sqlite`); + const config: InitConfig = { label: name, walletConfig: { id: name, key: name, + storage: { + type: "sqlite", + path: walletPath, + }, }, endpoints: [`http://localhost:${port}`], }; diff --git a/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/connections.ts b/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/connections.ts index c1cf3981c6..2a4cf7ec00 100644 --- a/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/connections.ts +++ b/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/connections.ts @@ -200,12 +200,12 @@ export async function connectAgents( * Block until given connection is operational. * * @param agent Aries agent - * @param outOfBandRecordId connection outOfBandRecordId + * @param outOfBandId connection outOfBandId * @param timeout operation timeout (will throw exception if timeout exceeded) */ -export async function waitForConnectionReady( +export async function waitForConnectionReadyV1( agent: Agent, - outOfBandRecordId: string, + outOfBandId: string, timeout = WAIT_FOR_CLIENT_ACCEPT_TIMEOUT, ): Promise { let connection: ConnectionRecord | undefined; @@ -218,11 +218,11 @@ export async function waitForConnectionReady( ); connection = ( - await agent.connections.findAllByOutOfBandId(outOfBandRecordId) + await agent.connections.findAllByOutOfBandId(outOfBandId) ).pop(); } while (counter > 0 && (!connection || !connection.isReady)); if (counter <= 0) { - throw new Error("waitForConnectionReady() timeout reached!"); + throw new Error("waitForConnectionReadyV1() timeout reached!"); } } diff --git a/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/credentials.ts b/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/credentials.ts index 96c2883392..43ed66d192 100644 --- a/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/credentials.ts +++ b/examples/cactus-example-discounted-asset-trade-client/src/main/typescript/lib/credentials.ts @@ -128,6 +128,7 @@ export async function registerCredentialDefinition( options: { endorserMode: "internal", endorserDid: did, + supportRevocation: false, }, }); diff --git a/examples/cactus-example-discounted-asset-trade/README.md b/examples/cactus-example-discounted-asset-trade/README.md index 3f0e40fe2c..9c90484cc9 100644 --- a/examples/cactus-example-discounted-asset-trade/README.md +++ b/examples/cactus-example-discounted-asset-trade/README.md @@ -66,6 +66,13 @@ Alice knows that Acme Corp. provides digital certificates. She asks Acme Corp. t popd ``` +1. Create settings directory and make sure you have write permission on it + + ```bash + sudo mkdir /etc/cactus + sudo chown -R __YOUR_USERNAME__ /etc/cactus/ + ``` + 1. Start the ledgers: ``` @@ -84,7 +91,7 @@ Alice knows that Acme Corp. provides digital certificates. She asks Acme Corp. t - Use `setup-credentials` script from `cactus-example-discounted-asset-trade-client`. ```bash -# In separat shell (can be used later for client app) +# In separate shell (can be used later for client app) cd ./examples/cactus-example-discounted-asset-trade-client yarn setup-credentials popd @@ -294,33 +301,43 @@ Action: Get assets 1. Press `Ctrl+C` in `docker-compose` console to stop the application. 1. Run cleanup script - ``` - sudo ./script-cleanup.sh # for root owned files - ./script-cleanup.sh # for user owner files - ``` +``` + +sudo ./script-cleanup.sh # for root owned files +./script-cleanup.sh # for user owner files + +``` #### Manual cleanup instructions 1. Remove the config files on your machine - ``` - sudo rm -r ./etc/cactus/ - ``` +``` + +sudo rm -r ./etc/cactus/ + +``` 1. Stop the docker containers of Ethereum, Fabric and Indy - - `docker stop geth1 asset_trade_faio2x_testnet asset_trade_indy_all_in_one` - - `docker rm geth1 asset_trade_faio2x_testnet asset_trade_indy_all_in_one` +- `docker stop geth1 asset_trade_faio2x_testnet asset_trade_indy_all_in_one` +- `docker rm geth1 asset_trade_faio2x_testnet asset_trade_indy_all_in_one` 1. Clear indy-all-in-one - ``` - pushd ../../tools/docker/indy-all-in-one/ - ./script-cleanup.sh - popd - ``` +``` + +pushd ../../tools/docker/indy-all-in-one/ +./script-cleanup.sh +popd + +``` 1. Remove geth files - ``` - pushd ../../tools/docker/geth-testnet/ - rm -fr ./data-geth1/geth/ - popd - ``` +``` + +pushd ../../tools/docker/geth-testnet/ +rm -fr ./data-geth1/geth/ +popd + +``` + +``` diff --git a/examples/cactus-example-discounted-asset-trade/aries-connector.ts b/examples/cactus-example-discounted-asset-trade/aries-connector.ts new file mode 100644 index 0000000000..1cb5497aad --- /dev/null +++ b/examples/cactus-example-discounted-asset-trade/aries-connector.ts @@ -0,0 +1,152 @@ +import { IListenOptions, Servers } from "@hyperledger/cactus-common"; +import { Constants, Configuration } from "@hyperledger/cactus-core-api"; +import { ConfigUtil } from "@hyperledger/cactus-cmd-socketio-server"; +import { + PluginLedgerConnectorAries, + AriesApiClient, +} from "@hyperledger/cactus-plugin-ledger-connector-aries"; + +import * as path from "node:path"; +import * as os from "node:os"; +import http from "http"; +import express from "express"; +import bodyParser from "body-parser"; +import { readFileSync } from "fs"; +import { AddressInfo } from "net"; +import { v4 as uuidv4 } from "uuid"; +import { getLogger } from "log4js"; +import { Server as SocketIoServer } from "socket.io"; + +const config: any = ConfigUtil.getConfig(); +const moduleName = "aries-connector"; +const logger = getLogger(`${moduleName}`); +logger.level = config.logLevel; + +// Constants +const BLP_AGENT_NAME = "cactiDiscountedAssetTradeAgent"; +const BLP_AGENT_INBOUND_URL = "http://127.0.0.1:5035"; +const DID_INDY_NAMESPACE = "cacti:test"; + +const ARIES_WALLET_PATH = path.join( + os.homedir(), + ".cacti/cactus-example-discounted-asset-trade/wallet", +); + +// Read Genesis transactions +const genesisTransactionsPath = + "/etc/cactus/indy-all-in-one/pool_transactions_genesis"; +logger.info( + "Reading Indy genesis transactions from file:", + genesisTransactionsPath, +); +const genesisTransactions = readFileSync(genesisTransactionsPath).toString( + "utf-8", +); + +/** + * Configuration for local indy-all-in-one ledger. + */ +export const localTestNetwork = { + isProduction: false, + genesisTransactions, + indyNamespace: DID_INDY_NAMESPACE, + connectOnStartup: true, +}; + +// Single Aries connector instance +let ariesConnectorPlugin: PluginLedgerConnectorAries | undefined = undefined; +let ariesApiClient: AriesApiClient | undefined = undefined; + +async function createAriesConnector() { + if (ariesConnectorPlugin) { + ariesConnectorPlugin.shutdown(); + ariesConnectorPlugin = undefined; + } + + ariesConnectorPlugin = new PluginLedgerConnectorAries({ + instanceId: `ariesAssetTrade-${uuidv4()}`, + logLevel: config.logLevel, + walletPath: ARIES_WALLET_PATH, + ariesAgents: [ + { + name: BLP_AGENT_NAME, + walletKey: BLP_AGENT_NAME, + indyNetworks: [localTestNetwork], + inboundUrl: BLP_AGENT_INBOUND_URL, + autoAcceptConnections: true, + }, + ], + }); + + await ariesConnectorPlugin.onPluginInit(); + + // Run http server + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + const connectorServer = http.createServer(expressApp); + const listenOptions: IListenOptions = { + hostname: "127.0.0.1", + port: 0, + server: connectorServer, + }; + const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + const apiHost = `http://${addressInfo.address}:${addressInfo.port}`; + + // Run socketio server + const socketioServer = new SocketIoServer(connectorServer, { + path: Constants.SocketIoConnectionPathV1, + }); + + // Register services + await ariesConnectorPlugin.getOrCreateWebServices(); + await ariesConnectorPlugin.registerWebServices(expressApp, socketioServer); + + // Create ApiClient + const apiConfig = new Configuration({ basePath: apiHost }); + ariesApiClient = new AriesApiClient(apiConfig); +} + +/** + * Create aries connector + */ +export async function initAriesConnector(): Promise { + if (!ariesConnectorPlugin) { + await createAriesConnector(); + logger.info("initAriesConnector() done."); + } else { + logger.info("initAriesConnector() Aries connector already initialized"); + } +} + +/** + * Get instance of aries connector, initialize it if not done yet. + */ +export async function getAriesConnector(): Promise { + if (!ariesConnectorPlugin) { + await initAriesConnector(); + } + + if (ariesConnectorPlugin) { + return ariesConnectorPlugin; + } else { + throw new Error("Could not initialize new aries connector!"); + } +} + +/** + * Get instance of aries api client. + */ +export function getAriesApiClient(): AriesApiClient { + if (ariesApiClient) { + return ariesApiClient; + } else { + throw new Error("Aries connector not initialized yet!"); + } +} + +/** + * Get BLP agent name registered in connector + */ +export function getBlpAgentName(): string { + return BLP_AGENT_NAME; +} diff --git a/examples/cactus-example-discounted-asset-trade/indy-agent.ts b/examples/cactus-example-discounted-asset-trade/indy-agent.ts deleted file mode 100644 index 80feaf5377..0000000000 --- a/examples/cactus-example-discounted-asset-trade/indy-agent.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Defines Indy Aries JS Agent that will be used to verify proofs. - * TODO - Initialize Indy connector instead of agent here (once indy connector is ready) - */ - -import { getLogger } from "log4js"; -import { ConfigUtil } from "@hyperledger/cactus-cmd-socketio-server"; -import { - setupAgent, - AnoncredAgent, -} from "@hyperledger/cactus-example-discounted-asset-trade-client"; - -const config: any = ConfigUtil.getConfig(); -const moduleName = "indy-agent"; -const logger = getLogger(`${moduleName}`); -logger.level = config.logLevel; - -const BLP_AGENT_NAME = "cactiDiscountedAssetTradeAgent"; -const BLP_AGENT_PORT = 5035; - -// Single BLP indy agent instance -let blpAgent: AnoncredAgent | undefined = undefined; - -/** - * Create indy agent for this BLP app - */ -export async function initIndyAgent(): Promise { - if (!blpAgent) { - blpAgent = await setupAgent(BLP_AGENT_NAME, BLP_AGENT_PORT); - logger.info("initIndyAgent() done."); - } else { - logger.info("initIndyAgent() Indy agent already initialized"); - } -} - -/** - * Get instance of indy agent - */ -export async function getIndyAgent(): Promise { - if (!blpAgent) { - await initIndyAgent(); - } - - if (blpAgent) { - return blpAgent; - } else { - throw new Error("Could not initialize new indy agent!"); - } -} diff --git a/examples/cactus-example-discounted-asset-trade/package.json b/examples/cactus-example-discounted-asset-trade/package.json index 0be6bf96a1..bfcdc76187 100644 --- a/examples/cactus-example-discounted-asset-trade/package.json +++ b/examples/cactus-example-discounted-asset-trade/package.json @@ -19,8 +19,8 @@ "@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-example-discounted-asset-trade-client": "2.0.0-alpha.2", "@hyperledger/cactus-plugin-keychain-memory": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-aries": "2.0.0-alpha.2", "@hyperledger/cactus-plugin-ledger-connector-ethereum": "2.0.0-alpha.2", "@hyperledger/cactus-plugin-ledger-connector-fabric": "2.0.0-alpha.2", "@types/node": "14.18.54", diff --git a/examples/cactus-example-discounted-asset-trade/script-cleanup.sh b/examples/cactus-example-discounted-asset-trade/script-cleanup.sh index da35417c4d..1e1193152e 100755 --- a/examples/cactus-example-discounted-asset-trade/script-cleanup.sh +++ b/examples/cactus-example-discounted-asset-trade/script-cleanup.sh @@ -4,7 +4,8 @@ echo ">> Remove the config files on your machine" rm -rf ./etc/cactus/ -rm -rf /etc/cactus/indy-all-in-one/ +rm -rf /etc/cactus/* +rm -rf ~/.cacti/cactus-example-discounted-asset-trade/ echo ">> Stop the docker containers of Ethereum, Fabric and Indy" docker stop geth1 asset_trade_faio2x_testnet asset_trade_indy_all_in_one diff --git a/examples/cactus-example-discounted-asset-trade/transaction-indy.ts b/examples/cactus-example-discounted-asset-trade/transaction-indy.ts index ec4d6db383..feaf95a559 100644 --- a/examples/cactus-example-discounted-asset-trade/transaction-indy.ts +++ b/examples/cactus-example-discounted-asset-trade/transaction-indy.ts @@ -1,59 +1,53 @@ -/* - * TODO - Replace these methods with their counterparts in Indy connector (when it's ready). - * For now we import utility functions from @hyperledger/cactus-example-discounted-asset-trade-client for simplicity. - */ - import { getLogger } from "log4js"; import { ConfigUtil } from "@hyperledger/cactus-cmd-socketio-server"; -import { - acceptInvitation, - checkCredentialProof, - waitForConnectionReady, -} from "@hyperledger/cactus-example-discounted-asset-trade-client"; -import { getIndyAgent } from "./indy-agent"; +import { getAriesApiClient, getBlpAgentName } from "./aries-connector"; const config: any = ConfigUtil.getConfig(); - const moduleName = "transaction-indy"; const logger = getLogger(`${moduleName}`); logger.level = config.logLevel; /** - * Connect remote client indy agent using it's invitation URL. + * Connect remote client aries agent using it's invitation URL. * Wait until connection is established and ready to use. * - * @param invitationUrl anoncreds invitation URL. + * @param invitationUrl aries invitation URL. * @returns connection ID of newly created connection */ export async function connectToClientAgent( invitationUrl: string, ): Promise { logger.info("Accepting invitation from client agent..."); + const ariesApiClient = getAriesApiClient(); - const blpAgent = await getIndyAgent(); - const outOfBandRecord = await acceptInvitation(blpAgent, invitationUrl); - await waitForConnectionReady(blpAgent, outOfBandRecord.id); - - logger.debug( - "connectToClientAgent() - outOfBandRecord ID:", - outOfBandRecord.id, - ); - const [connection] = await blpAgent.connections.findAllByOutOfBandId( - outOfBandRecord.id, - ); + const acceptResponse = await ariesApiClient.acceptInvitationV1({ + agentName: getBlpAgentName(), + invitationUrl: invitationUrl, + }); + const { outOfBandId } = acceptResponse.data; + await ariesApiClient.waitForConnectionReadyV1(getBlpAgentName(), outOfBandId); + logger.debug("connectToClientAgent() - outOfBandId:", outOfBandId); - if (!connection) { - throw Error( + // Get connection ID + const connectionsResponse = await ariesApiClient.getConnectionsV1({ + agentName: getBlpAgentName(), + filter: { + outOfBandId, + }, + }); + const connectionRecord = connectionsResponse.data.pop(); + if (!connectionRecord) { + throw new Error( "Could not establish a connection to remote client indy agent!", ); } - logger.debug("connectToClientAgent() - connection ID:", connection.id); + logger.debug("connectToClientAgent() - connection ID:", connectionRecord.id); - return connection.id; + return connectionRecord.id; } /** - * Request and verify employment proof from client agent connected with specified ID. + * Request and verify employment proof from client agent with specified connection ID. * * @param indyAgentConId client agent connection ID. * @param credentialDefinitionId employment credential definition ID. @@ -62,14 +56,26 @@ export async function connectToClientAgent( export async function isEmploymentCredentialProofValid( indyAgentConId: string, credentialDefinitionId: string, -) { - const blpAgent = await getIndyAgent(); - const proof = await checkCredentialProof( - blpAgent, - credentialDefinitionId, - indyAgentConId, - ); - logger.debug("Received employment proof response:", proof); +): Promise { + const ariesApiClient = getAriesApiClient(); + + try { + const proof = await ariesApiClient.requestProofAndWaitV1( + getBlpAgentName(), + indyAgentConId, + [ + { + name: "employee_status", + isValueEqual: "Permanent", + isCredentialDefinitionIdEqual: credentialDefinitionId, + }, + ], + ); + logger.debug("Received employment proof response:", proof); + return proof.isVerified ?? false; + } catch (error) { + logger.warn("Error when requesting employment proof:", error); + } - return proof.state === "done"; + return false; } diff --git a/examples/cactus-example-discounted-asset-trade/tsconfig.json b/examples/cactus-example-discounted-asset-trade/tsconfig.json index d6c0d1c312..638cc48cef 100644 --- a/examples/cactus-example-discounted-asset-trade/tsconfig.json +++ b/examples/cactus-example-discounted-asset-trade/tsconfig.json @@ -24,13 +24,13 @@ "path": "../../packages/cactus-plugin-keychain-memory/tsconfig.json" }, { - "path": "../../packages/cactus-plugin-ledger-connector-fabric/tsconfig.json" + "path": "../../packages/cactus-plugin-ledger-connector-aries/tsconfig.json" }, { - "path": "../../packages/cactus-plugin-ledger-connector-ethereum/tsconfig.json" + "path": "../../packages/cactus-plugin-ledger-connector-fabric/tsconfig.json" }, { - "path": "../../examples/cactus-example-discounted-asset-trade-client/tsconfig.json" + "path": "../../packages/cactus-plugin-ledger-connector-ethereum/tsconfig.json" } ] } diff --git a/examples/cactus-example-discounted-asset-trade/www.ts b/examples/cactus-example-discounted-asset-trade/www.ts index a6f51b6775..3682887edd 100644 --- a/examples/cactus-example-discounted-asset-trade/www.ts +++ b/examples/cactus-example-discounted-asset-trade/www.ts @@ -2,13 +2,13 @@ import { BusinessLogicAssetTrade } from "./business-logic-asset-trade"; import { startCactusSocketIOServer } from "@hyperledger/cactus-cmd-socketio-server"; import { initFabricConnector } from "./fabric-connector"; import { initEthereumConnector } from "./ethereum-connector"; -import { initIndyAgent } from "./indy-agent"; +import { initAriesConnector } from "./aries-connector"; async function startBLP() { try { await initFabricConnector(); await initEthereumConnector(); - await initIndyAgent(); + await initAriesConnector(); startCactusSocketIOServer({ id: "guks32pf", diff --git a/packages/cactus-plugin-ledger-connector-aries/README.md b/packages/cactus-plugin-ledger-connector-aries/README.md new file mode 100644 index 0000000000..2e7b4dd9ef --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/README.md @@ -0,0 +1,188 @@ +# `@hyperledger/cactus-plugin-ledger-connector-aries` + +This plugin provides `Cacti` a way to interact with Aries agents and other aries connectors. Using this we can: + +- Connect with another Aries agents. +- Request proof from peer. +- Monitor connection and proof state changes. + +Limitations: + +- AnonCreds V2 protocols only. +- Indy VDR only. +- Only sqlite Askar wallet on the same machine as the connector. + +## Summary +- [Getting Started](#getting-started) +- [Usage](#usage) +- [AriesApiClient](#ariesapiclient) +- [Runing the tests](#running-the-tests) +- [Contributing](#contributing) +- [License](#license) +- [Acknowledgments](#acknowledgments) + +## Getting Started + +Clone the git repository on your local machine. Follow these instructions that will get you a copy of the project up and running on +your local machine for development and testing purposes. + +### Prerequisites + +In the root of the project to install the dependencies execute the command: + +```sh +npm run configure +``` + +## Usage + +To use this plugin, import public-api, create new **PluginLedgerConnectorAries** and initialize it. + +```typescript +const connector = new PluginLedgerConnectorAries({ + instanceId: uuidV4(), + logLevel: testLogLevel, + pluginRegistry: new PluginRegistry({ plugins: [] }), + walletPath: "/home/user/my-wallet", + invitationDomain: "https://example.org", + ariesAgents: [ + { + name: aliceAgentName, + walletKey: aliceAgentName, + indyNetworks: [await ledger.getIndyVdrPoolConfig(indyNamespace)], + inboundUrl: aliceInboundUrl, + autoAcceptConnections: true, + }, + { + name: bobAgentName, + walletKey: bobAgentName, + indyNetworks: [await ledger.getIndyVdrPoolConfig(indyNamespace)], + inboundUrl: bobInboundUrl, + autoAcceptConnections: true, + }, + ], +}); + +// Register endpoints +await connector.getOrCreateWebServices(); +await connector.registerWebServices(expressApp, wsApi); + +// Initialize Aries agents +await connector.onPluginInit(); +``` + +### Configuration + +#### Connector Setup + +- `instanceId` - unique ID of the connector +- `logLevel` - connector log level +- `walletPath` - Default Aries agents sqlite wallet location. + Under this location each agent will create its wallet using agent name. + Path can be set explicitly for any agent managed by the connector. +- `invitationDomain`: Invitations created by this connector will use invitationDomain. + Can be overwriten when creating invitation. +- `ariesAgents`: List of aries agent managed by the connector to be created on startup. + Additional agents can be configured during runtime with `addAriesAgent` connector method. + See `Agent Setup` section below for details on agent configuration. + +#### Agent Setup + +- `name`: Aries agent label that will also be used as wallet id. +- `walletKey`: Wallet private key - do not share with anyone. +- `walletPath`: Path to wallet sqlite database to use. If not provided, the connector default path and agent name will be used. +- `indyNetworks`: List of indy networks to connect the agent to. +- `inboundUrl`: Inbound endpoint URL for this agent. Must be unique for this connector. Must contain port. +- `autoAcceptConnections`: Flag to accept new connection by default. +- `autoAcceptCredentials`: Policy of accepting credentials. +- `autoAcceptProofs`: Policy of accepting proof requests. + +> **Warning - Currently `autoAcceptConnections`, `autoAcceptCredentials`, and `autoAcceptProofs` are ignored! The defaults are used instead (autoAcceptConnections - `true`, credential and proof policies - `ContentApproved`)** + +### Connector Methods + +- Connector can be used directly through it's public methods. +- Most methods require `agentName` to identify Aries agent which should perform operation (single connector can control many agents). + +#### Methods + +```typescript +// Managing Agents +public async getAgents(): Promise +public async getAriesAgentOrThrow(agentName: string): Promise +public async addAriesAgent(agentConfig: AriesAgentConfigV1): Promise +public async removeAriesAgent(agentName: string): Promise +public async importExistingIndyDidFromPrivateKey(agentName: string, seed: string, indyNamespace: string): Promise + +// Connecting +public async getConnections(agentName: string, filter: AgentConnectionsFilterV1 = {}): Promise +public async createNewConnectionInvitation(agentName: string, invitationDomain?: string) +public async acceptInvitation(agentName: string, invitationUrl: string) + +// Proof requesting +public async requestProof(agentName: string, connectionId: string, proofAttributes: CactiProofRequestAttributeV1[]) +``` + +## AriesApiClient + +All connector API endpoints are defined in [open-api specification](./src/main/json/openapi.json). You can use [AriesApiClient](./src/main/typescript/api-client) to call remote aries connector functions. It also contain additional utility functions to ease integration. + +See [DefaultApi](./src/main/typescript/generated/openapi/typescript-axios/api.ts) and [AriesApiClient](./src/main/typescript/api-client/aries-api-client.ts) +for up-to-date listing of supported endpoints. + +### REST Functions + +- `getAgentsV1` +- `getConnectionsV1` +- `createNewConnectionInvitationV1` +- `acceptInvitationV1` +- `requestProofV1` + +### Monitoring methods + +- `watchConnectionStateV1` +- `watchProofStateV1` + +### Helper methods + +- `waitForConnectionReadyV1` +- `waitForInvitedPeerConnectionV1` +- `waitForProofCompletionV1` +- `requestProofAndWaitV1` + +## Running the tests + +To check that all has been installed correctly and that the plugin has no errors run jest test suites. + +- Run this command at the project's root: + +```sh +npx jest cactus-plugin-ledger-connector-aries +``` + +### Example apps +- [Discounted Asset Trade](../../examples/cactus-example-discounted-asset-trade/README.md) sample app uses aries connector to verify remote aries agent employment proof. + +## Contributing + +We welcome contributions to Hyperledger Cactus in many forms, and there’s always plenty to do! + +Please review [CONTIRBUTING.md](../../CONTRIBUTING.md) to get started. + +### TODO + +- Add option of connecting cacti aries agents directly (invitation / accept performed by connector directly). +- Add create credential option (will register schema and credental definition when necessary). +- Add issue credential option. +- Implement own aries wallet plugin that will use our keychain plugin as secure storage. +- Write functional test for proof verification. + +## License + +This distribution is published under the Apache License Version 2.0 found in the [LICENSE](../../LICENSE) file. + +## Acknowledgments + +``` + +``` diff --git a/packages/cactus-plugin-ledger-connector-aries/openapitools.json b/packages/cactus-plugin-ledger-connector-aries/openapitools.json new file mode 100644 index 0000000000..225ff1aaae --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/openapitools.json @@ -0,0 +1,7 @@ +{ + "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "6.6.0" + } +} diff --git a/packages/cactus-plugin-ledger-connector-aries/package.json b/packages/cactus-plugin-ledger-connector-aries/package.json new file mode 100644 index 0000000000..95458e1828 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/package.json @@ -0,0 +1,102 @@ +{ + "name": "@hyperledger/cactus-plugin-ledger-connector-aries", + "version": "2.0.0-alpha.2", + "description": "Allows Cactus nodes to connect to an Indy ledger and Aries agents.", + "keywords": [ + "Hyperledger", + "Cacti", + "Cactus", + "Integration", + "Blockchain", + "Distributed Ledger Technology" + ], + "homepage": "https://github.com/hyperledger/cacti#readme", + "bugs": { + "url": "https://github.com/hyperledger/cacti/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hyperledger/cacti.git" + }, + "license": "Apache-2.0", + "author": { + "name": "Hyperledger Cacti Contributors", + "email": "cacti@lists.hyperledger.org", + "url": "https://www.hyperledger.org/use/cacti" + }, + "contributors": [ + { + "name": "Please add yourself to the list of contributors", + "email": "your.name@example.com", + "url": "https://example.com" + }, + { + "name": "Michal Bajer", + "email": "michal.bajer@fujitsu.com", + "url": "https://www.fujitsu.com/global/" + } + ], + "main": "dist/lib/main/typescript/index.js", + "module": "dist/lib/main/typescript/index.js", + "browser": "dist/cactus-plugin-ledger-connector-aries.web.umd.js", + "types": "dist/lib/main/typescript/index.d.ts", + "files": [ + "dist/*" + ], + "scripts": { + "codegen": "run-p 'codegen:*'", + "codegen:openapi": "npm run generate-sdk", + "generate-sdk": "run-p 'generate-sdk:*'", + "generate-sdk:typescript-axios": "openapi-generator-cli generate -i ./src/main/json/openapi.json -g typescript-axios -o ./src/main/typescript/generated/openapi/typescript-axios/ --ignore-file-override ../../openapi-generator-ignore --reserved-words-mappings protected=protected", + "watch": "npm-watch", + "webpack": "npm-run-all webpack:dev", + "webpack:dev": "npm-run-all webpack:dev:node webpack:dev:web", + "webpack:dev:node": "webpack --env=dev --target=node --config ../../webpack.config.js", + "webpack:dev:web": "webpack --env=dev --target=web --config ../../webpack.config.js" + }, + "dependencies": { + "@aries-framework/anoncreds": "0.5.0-alpha.71", + "@aries-framework/anoncreds-rs": "0.5.0-alpha.71", + "@aries-framework/askar": "0.5.0-alpha.71", + "@aries-framework/core": "0.5.0-alpha.71", + "@aries-framework/indy-vdr": "0.5.0-alpha.71", + "@aries-framework/node": "0.5.0-alpha.71", + "@hyperledger/anoncreds-nodejs": "0.2.0-dev.4", + "@hyperledger/aries-askar-nodejs": "0.2.0-dev.1", + "@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/indy-vdr-nodejs": "0.2.0-dev.3", + "axios": "1.6.0", + "rxjs": "7.8.1", + "socket.io-client-fixed-types": "4.5.4" + }, + "devDependencies": { + "@hyperledger/cactus-test-tooling": "2.0.0-alpha.2", + "@types/body-parser": "1.19.4", + "@types/express": "4.17.19", + "@types/uuid": "9.0.6", + "body-parser": "1.20.2", + "express": "4.18.2", + "jest": "29.6.2", + "jest-extended": "4.0.1", + "socket.io": "4.5.4", + "uuid": "9.0.1" + }, + "engines": { + "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": [ + "./src/main/json/openapi.json" + ] + } + } +} 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 new file mode 100644 index 0000000000..40dc6a327d --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/json/openapi.json @@ -0,0 +1,745 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Hyperledger Cacti Plugin - Connector Aries", + "description": "Can communicate with other Aries agents and Cacti Aries connectors", + "version": "v2.0.0-alpha.2", + "license": { + "name": "Apache-2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "components": { + "schemas": { + "WatchConnectionStateV1": { + "type": "string", + "description": "Websocket requests for monitoring connection change events.", + "enum": [ + "org.hyperledger.cactus.api.async.hlaries.WatchConnectionStateV1.Subscribe", + "org.hyperledger.cactus.api.async.hlaries.WatchConnectionStateV1.Next", + "org.hyperledger.cactus.api.async.hlaries.WatchConnectionStateV1.Unsubscribe", + "org.hyperledger.cactus.api.async.hlaries.WatchConnectionStateV1.Error", + "org.hyperledger.cactus.api.async.hlaries.WatchConnectionStateV1.Complete" + ], + "x-enum-varnames": [ + "Subscribe", + "Next", + "Unsubscribe", + "Error", + "Complete" + ] + }, + "WatchConnectionStateOptionsV1": { + "type": "object", + "description": "Options passed when monitoring connection change events.", + "required": ["agentName"], + "properties": { + "agentName": { + "type": "string", + "nullable": false, + "description": "Aries agent label that will also be used as wallet id." + } + } + }, + "WatchConnectionStateProgressV1": { + "type": "object", + "description": "Values pushed on each connection state change.", + "required": ["connectionRecord", "previousState"], + "properties": { + "connectionRecord": { + "$ref": "#/components/schemas/AgentConnectionRecordV1", + "nullable": false + }, + "previousState": { + "type": "string", + "nullable": true + } + } + }, + "WatchProofStateV1": { + "type": "string", + "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", + "org.hyperledger.cactus.api.async.hlaries.WatchProofStateV1.Unsubscribe", + "org.hyperledger.cactus.api.async.hlaries.WatchProofStateV1.Error", + "org.hyperledger.cactus.api.async.hlaries.WatchProofStateV1.Complete" + ], + "x-enum-varnames": [ + "Subscribe", + "Next", + "Unsubscribe", + "Error", + "Complete" + ] + }, + "WatchProofStateOptionsV1": { + "type": "object", + "description": "Options passed when monitoring proof state change events.", + "required": ["agentName"], + "properties": { + "agentName": { + "type": "string", + "nullable": false, + "description": "Aries agent label that will also be used as wallet id." + } + } + }, + "WatchProofStateProgressV1": { + "type": "object", + "description": "Values pushed on each proof state change.", + "required": ["proofRecord", "previousState"], + "properties": { + "proofRecord": { + "$ref": "#/components/schemas/AriesProofExchangeRecordV1", + "nullable": false + }, + "previousState": { + "type": "string", + "nullable": true + } + } + }, + "CactiAcceptPolicyV1": { + "type": "string", + "description": "Credential / Proof requests acceptance policies for Aries agent", + "enum": ["always", "contentApproved", "never"], + "x-enum-varnames": ["Always", "ContentApproved", "Never"] + }, + "CactiProofRequestAttributeV1": { + "type": "object", + "description": "Credential attribute checks to be performed by a proof request.", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "nullable": false, + "description": "Attribute name." + }, + "isValueEqual": { + "nullable": false, + "description": "Check if attribute has specified value" + }, + "isCredentialDefinitionIdEqual": { + "nullable": false, + "description": "Check if credentialDefinitionId has specified value" + } + } + }, + "AriesProofExchangeRecordV1": { + "type": "object", + "description": "Proof exchange record from Aries framework (simplified)", + "additionalProperties": true, + "required": ["id", "threadId", "state", "protocolVersion"], + "properties": { + "id": { + "type": "string", + "nullable": false + }, + "connectionId": { + "type": "string", + "nullable": false + }, + "threadId": { + "type": "string", + "nullable": false + }, + "state": { + "type": "string", + "nullable": false + }, + "protocolVersion": { + "type": "string", + "nullable": false + }, + "isVerified": { + "type": "boolean", + "nullable": false + }, + "errorMessage": { + "type": "string", + "nullable": false + } + } + }, + "AriesIndyVdrPoolConfigV1": { + "type": "object", + "description": "Indy VDR network configuration", + "required": ["genesisTransactions", "isProduction", "indyNamespace"], + "properties": { + "genesisTransactions": { + "type": "string", + "nullable": false, + "description": "Indy genesis transactions." + }, + "isProduction": { + "type": "boolean", + "nullable": false, + "description": "Flag to specify whether this is production or development ledger." + }, + "indyNamespace": { + "type": "string", + "nullable": false, + "description": "Indy namespace" + }, + "connectOnStartup": { + "type": "boolean", + "nullable": false, + "description": "Connect to the ledger on startup flag" + } + } + }, + "AriesAgentConfigV1": { + "type": "object", + "description": "Aries agent configuration to be setup and used by the connector.", + "required": ["name", "walletKey", "indyNetworks"], + "properties": { + "name": { + "type": "string", + "nullable": false, + "description": "Aries agent label that will also be used as wallet id." + }, + "walletKey": { + "type": "string", + "nullable": false, + "description": "Wallet private key - do not share with anyone." + }, + "walletPath": { + "type": "string", + "nullable": false, + "description": "Path to wallet sqlite database to use. If not provided, the connector default path and agent name will be used." + }, + "indyNetworks": { + "type": "array", + "nullable": false, + "items": { + "$ref": "#/components/schemas/AriesIndyVdrPoolConfigV1", + "minItems": 1 + } + }, + "inboundUrl": { + "type": "string", + "nullable": false, + "description": "Inbound endpoint URL for this agent. Must be unique for this connector. Must contain port.", + "example": "http://127.0.0.1:1234", + "default": "undefined" + }, + "autoAcceptConnections": { + "type": "boolean", + "nullable": false, + "description": "Flag to accept new connection by default", + "default": "false" + }, + "autoAcceptCredentials": { + "$ref": "#/components/schemas/CactiAcceptPolicyV1", + "nullable": false, + "description": "Policy for accepting new credentials by this agent", + "default": "never" + }, + "autoAcceptProofs": { + "$ref": "#/components/schemas/CactiAcceptPolicyV1", + "nullable": false, + "description": "Policy for accepting new proof requests by this agent", + "default": "never" + } + } + }, + "AriesAgentSummaryV1": { + "type": "object", + "description": "Summary of an Aries Agent configured in the connector.", + "required": [ + "name", + "isAgentInitialized", + "isWalletInitialized", + "isWalletProvisioned", + "walletConfig", + "endpoints" + ], + "properties": { + "name": { + "type": "string", + "nullable": false, + "description": "Aries label of an agent" + }, + "isAgentInitialized": { + "type": "boolean", + "nullable": false, + "description": "True when Aries agent has been initialized properly." + }, + "isWalletInitialized": { + "type": "boolean", + "nullable": false, + "description": "True when this agents wallet has been initialized properly." + }, + "isWalletProvisioned": { + "type": "boolean", + "nullable": false, + "description": "True when this agents wallet has been provisioned properly." + }, + "walletConfig": { + "type": "object", + "nullable": false, + "required": ["id", "type"], + "properties": { + "id": { + "type": "string", + "nullable": false, + "description": "Wallet entry ID" + }, + "type": { + "type": "string", + "nullable": false, + "description": "Wallet storage type" + } + } + }, + "endpoints": { + "type": "array", + "nullable": false, + "description": "Aries agent endpoints configured", + "items": { + "type": "string", + "nullable": false + } + } + } + }, + "AgentConnectionsFilterV1": { + "type": "object", + "description": "Fields that can be used to filter agent connection list.", + "properties": { + "did": { + "type": "string", + "nullable": false + }, + "invitationDid": { + "type": "string", + "nullable": false + }, + "outOfBandId": { + "type": "string", + "nullable": false + }, + "role": { + "type": "string", + "nullable": false + }, + "state": { + "type": "string", + "nullable": false + }, + "theirDid": { + "type": "string", + "nullable": false + }, + "threadId": { + "type": "string", + "nullable": false + } + } + }, + "AgentConnectionRecordV1": { + "type": "object", + "description": "Aries agent connection information.", + "additionalProperties": true, + "required": ["state", "role", "isReady"], + "properties": { + "state": { + "type": "string", + "nullable": false + }, + "role": { + "type": "string", + "nullable": false + }, + "isReady": { + "type": "boolean", + "nullable": false + }, + "did": { + "type": "string", + "nullable": false + }, + "theirDid": { + "type": "string", + "nullable": false + }, + "theirLabel": { + "type": "string", + "nullable": false + }, + "alias": { + "type": "string", + "nullable": false + }, + "threadId": { + "type": "string", + "nullable": false + }, + "errorMessage": { + "type": "string", + "nullable": false + }, + "outOfBandId": { + "type": "string", + "nullable": false + }, + "invitationDid": { + "type": "string", + "nullable": false + } + } + }, + "GetConnectionsV1Request": { + "type": "object", + "description": "Request for GetConnections endpoint.", + "required": ["agentName"], + "properties": { + "agentName": { + "type": "string", + "nullable": false + }, + "filter": { + "$ref": "#/components/schemas/AgentConnectionsFilterV1", + "nullable": false + } + } + }, + "GetConnectionsV1Response": { + "type": "array", + "description": "Response for GetConnections endpoint.", + "default": [], + "items": { + "$ref": "#/components/schemas/AgentConnectionRecordV1" + } + }, + "GetAgentsV1Response": { + "type": "array", + "description": "Response for GetAgents endpoint.", + "default": [], + "items": { + "$ref": "#/components/schemas/AriesAgentSummaryV1" + } + }, + "CreateNewConnectionInvitationV1Request": { + "type": "object", + "description": "Request for CreateNewConnectionInvitation endpoint.", + "required": ["agentName"], + "properties": { + "agentName": { + "type": "string", + "description": "Aries label of an agent to use to generate an invitation", + "nullable": false + }, + "invitationDomain": { + "type": "string", + "description": "Invitation URL domain to use. If not specified, then connector default domain will be used", + "nullable": false + } + } + }, + "CreateNewConnectionInvitationV1Response": { + "type": "object", + "description": "Response for CreateNewConnectionInvitation endpoint.", + "required": ["invitationUrl", "outOfBandId"], + "properties": { + "invitationUrl": { + "type": "string", + "description": "Invitation URL that can be used by another aries agent to connect to us.", + "nullable": false + }, + "outOfBandId": { + "type": "string", + "description": "ID that can be used to track status of the connection", + "nullable": false + } + } + }, + "AcceptInvitationV1Request": { + "type": "object", + "description": "Request for AcceptInvitation endpoint.", + "required": ["agentName", "invitationUrl"], + "properties": { + "agentName": { + "type": "string", + "description": "Aries label of an agent to be used to connect using URL", + "nullable": false + }, + "invitationUrl": { + "type": "string", + "description": "Invitation URL generated by another aries agent.", + "nullable": false + } + } + }, + "AcceptInvitationV1Response": { + "type": "object", + "description": "Response for AcceptInvitation endpoint.", + "required": ["outOfBandId"], + "properties": { + "outOfBandId": { + "type": "string", + "description": "ID that can be used to track status of the connection", + "nullable": false + } + } + }, + "RequestProofV1Request": { + "type": "object", + "description": "Request for RequestProof endpoint.", + "required": ["agentName", "connectionId", "proofAttributes"], + "properties": { + "agentName": { + "type": "string", + "description": "Aries label of an agent to be used to connect using URL", + "nullable": false + }, + "connectionId": { + "type": "string", + "description": "Peer connection ID from which we want to request a proof.", + "nullable": false + }, + "proofAttributes": { + "type": "array", + "nullable": false, + "items": { + "$ref": "#/components/schemas/CactiProofRequestAttributeV1", + "minItems": 1 + } + } + } + }, + "ErrorExceptionV1Response": { + "type": "object", + "description": "Error response from the connector.", + "required": ["message", "error"], + "properties": { + "message": { + "type": "string", + "description": "Short error description message.", + "nullable": false + }, + "error": { + "type": "string", + "description": "Detailed error information.", + "nullable": false + } + } + } + } + }, + "paths": { + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/get-agents": { + "post": { + "x-hyperledger-cacti": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/get-agents" + } + }, + "operationId": "getAgentsV1", + "summary": "Get all Aries agents configured in this connector plugin.", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetAgentsV1Response" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionV1Response" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/get-connections": { + "post": { + "x-hyperledger-cacti": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/get-connections" + } + }, + "operationId": "getConnectionsV1", + "summary": "Get all connections of given aries agent.", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetConnectionsV1Request" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetConnectionsV1Response" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionV1Response" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/create-new-connection-invitation": { + "post": { + "x-hyperledger-cacti": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/create-new-connection-invitation" + } + }, + "operationId": "createNewConnectionInvitationV1", + "summary": "Create new aries agent invitation that other agents can use to connect.", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateNewConnectionInvitationV1Request" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateNewConnectionInvitationV1Response" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionV1Response" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/accept-invitation": { + "post": { + "x-hyperledger-cacti": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/accept-invitation" + } + }, + "operationId": "acceptInvitationV1", + "summary": "Connect to another agent using it's invitation URL", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AcceptInvitationV1Request" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AcceptInvitationV1Response" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionV1Response" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/request-proof": { + "post": { + "x-hyperledger-cacti": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/request-proof" + } + }, + "operationId": "requestProofV1", + "summary": "Request proof matching provided requriements from connected peer agent.", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestProofV1Request" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AriesProofExchangeRecordV1" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionV1Response" + } + } + } + } + } + } + } + } +} 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 new file mode 100644 index 0000000000..6fc3fed8fe --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/api-client/aries-api-client.ts @@ -0,0 +1,427 @@ +import { Observable, ReplaySubject } from "rxjs"; +import { finalize } from "rxjs/operators"; +import { io } from "socket.io-client-fixed-types"; +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, + CactiProofRequestAttributeV1, + DefaultApi, + WatchConnectionStateOptionsV1, + WatchConnectionStateProgressV1, + WatchConnectionStateV1, + WatchProofStateOptionsV1, + WatchProofStateProgressV1, + WatchProofStateV1, +} from "../generated/openapi/typescript-axios"; +import { Configuration } from "../generated/openapi/typescript-axios/configuration"; + +export class AriesApiClientOptions extends Configuration { + readonly logLevel?: LogLevelDesc; + readonly wsApiHost?: string; + 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; + +export class AriesApiClient extends DefaultApi { + private readonly log: Logger; + private readonly wsApiHost: string; + private readonly wsApiPath: string; + + public get className(): string { + return "AriesApiClient"; + } + + constructor(public readonly options: AriesApiClientOptions) { + super(options); + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.wsApiHost = options.wsApiHost || options.basePath || location.host; + this.wsApiPath = options.wsApiPath || Constants.SocketIoConnectionPathV1; + this.log.debug(`Created ${this.className} OK.`); + this.log.debug(`wsApiHost=${this.wsApiHost}`); + this.log.debug(`wsApiPath=${this.wsApiPath}`); + 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 { + const socket = io(this.wsApiHost, { path: this.wsApiPath }); + const subject = new ReplaySubject(0); + + socket.on( + WatchConnectionStateV1.Next, + (data: WatchConnectionStateProgressV1) => { + this.log.debug("Received WatchConnectionStateV1.Next"); + subject.next(data); + }, + ); + + socket.on(WatchConnectionStateV1.Error, (ex: string) => { + this.log.warn("Received WatchConnectionStateV1.Error:", ex); + subject.error(ex); + }); + + socket.on(WatchConnectionStateV1.Complete, () => { + this.log.debug("Received WatchConnectionStateV1.Complete"); + subject.complete(); + }); + + socket.on("connect", () => { + this.log.info( + "Connected OK, sending WatchConnectionStateV1.Subscribe request...", + ); + socket.emit(WatchConnectionStateV1.Subscribe, options); + }); + + socket.connect(); + + return subject.pipe( + finalize(() => { + this.log.info("FINALIZE - unsubscribing from the stream..."); + socket.emit(WatchConnectionStateV1.Unsubscribe); + socket.close(); + }), + ); + } + + /** + * 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 { + const socket = io(this.wsApiHost, { path: this.wsApiPath }); + const subject = new ReplaySubject(0); + + socket.on(WatchProofStateV1.Next, (data: WatchProofStateProgressV1) => { + this.log.debug("Received WatchProofStateV1.Next"); + subject.next(data); + }); + + socket.on(WatchProofStateV1.Error, (ex: string) => { + this.log.warn("Received WatchProofStateV1.Error:", ex); + subject.error(ex); + }); + + socket.on(WatchProofStateV1.Complete, () => { + this.log.debug("Received WatchProofStateV1.Complete"); + subject.complete(); + }); + + socket.on("connect", () => { + this.log.info( + "Connected OK, sending WatchProofStateV1.Subscribe request...", + ); + socket.emit(WatchProofStateV1.Subscribe, options); + }); + + socket.connect(); + + return subject.pipe( + finalize(() => { + this.log.info("FINALIZE - unsubscribing from the stream..."); + socket.emit(WatchProofStateV1.Unsubscribe); + socket.close(); + }), + ); + } + + /** + * 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, + "outOfBandId", + outOfBandId, + ); + + return new Promise(async (resolve, reject) => { + // Common cleanup method for leaving the method + const cleanup = () => { + if (conStateSubscription) { + conStateSubscription.unsubscribe(); + } + + if (timeoutId) { + clearTimeout(timeoutId); + } + }; + + // Handle timeouts + const timeoutId = setTimeout(() => { + cleanup(); + reject( + new Error( + "waitForConnectionRecordV1() timeout - no connection found", + ), + ); + }, timeout); + + // Listen for connection establishment event + const conStateObservable = this.watchConnectionStateV1({ + agentName, + }); + const conStateSubscription = conStateObservable.subscribe((e) => { + if ( + e.connectionRecord.outOfBandId !== outOfBandId || + e.connectionRecord.state !== "completed" + ) { + return; + } + this.log.debug( + "waitForConnectionRecordV1() - received ConnectionStateChanged event for given outOfBandId", + ); + cleanup(); + resolve(); + }); + + // Also retrieve the connection record by invitation if the event has already fired + const connectionsResponse = await this.getConnectionsV1({ + agentName, + filter: { + outOfBandId, + }, + }); + const connection = connectionsResponse.data.pop(); + if (connection) { + this.log.debug( + "waitForConnectionRecordV1() - connection record already present", + ); + cleanup(); + resolve(); + } + }); + } + + /** + * 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, + ): Promise { + Checks.truthy(agentName, "waitForConnectionReadyV1 arg agentName"); + Checks.truthy(outOfBandId, "waitForConnectionReadyV1 arg outOfBandId"); + Checks.truthy( + timeout > 2 * WAIT_FOR_CONNECTION_READY_POLL_INTERVAL, + "waitForConnectionReadyV1 arg timeout to small", + ); + let connection: AgentConnectionRecordV1 | undefined; + let counter = Math.ceil(timeout / WAIT_FOR_CONNECTION_READY_POLL_INTERVAL); + this.log.debug( + `waitForConnectionReadyV1() timeout ${timeout}, retry ${counter} times...`, + ); + + do { + try { + counter--; + await new Promise((resolve) => + setTimeout(resolve, WAIT_FOR_CONNECTION_READY_POLL_INTERVAL), + ); + + const connections = await this.getConnectionsV1({ + agentName, + filter: { + outOfBandId, + }, + }); + connection = connections.data.pop(); + } catch (error) { + this.log.error("waitForConnectionReadyV1() error:", error); + } + } while (counter > 0 && (!connection || !connection.isReady)); + + if (counter <= 0) { + throw new Error( + `waitForConnectionReadyV1() agent ${agentName} oobId ${outOfBandId} timeout reached!`, + ); + } + + this.log.info( + `waitForConnectionReadyV1() agent ${agentName} oobId ${outOfBandId} OK!`, + ); + } + + /** + * 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, + ): Promise { + Checks.truthy(agentName, "waitForInvitedPeerConnectionV1 arg agentName"); + Checks.truthy( + outOfBandId, + "waitForInvitedPeerConnectionV1 arg outOfBandId", + ); + Checks.truthy( + timeout > 2 * WAIT_FOR_CONNECTION_READY_POLL_INTERVAL, + "waitForInvitedPeerConnectionV1 arg timeout to small", + ); + + await this.waitForConnectionRecordV1(agentName, outOfBandId, timeout); + this.log.info("waitForInvitedPeerConnectionV1() - connection record found"); + + await this.waitForConnectionReadyV1(agentName, outOfBandId, timeout); + } + + /** + * 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 = () => { + if (proofStateSubscription) { + proofStateSubscription.unsubscribe(); + } + + if (timeoutId) { + clearTimeout(timeoutId); + } + }; + + const timeoutId = setTimeout(() => { + cleanup(); + reject( + new Error( + "Timeout reached - could not receive proof confirmation from peer", + ), + ); + }, timeout); + + // Listener for proof state changes + const proofStateObservable = this.watchProofStateV1({ + agentName, + }); + const proofStateSubscription = proofStateObservable.subscribe((e) => { + const { id, state } = e.proofRecord; + if (id === proofId) { + this.log.debug( + "Found proof state change with a matching ID", + e.proofRecord.id, + e.proofRecord.state, + ); + + if (state === ProofState.Abandoned || state === ProofState.Declined) { + cleanup(); + reject( + new Error( + `Proof ${id} was rejected! ${JSON.stringify(e.proofRecord)}`, + ), + ); + } + + if (state === ProofState.Done) { + cleanup(); + this.log.info(`Proof ${id} was accepted by the peer`); + resolve(e.proofRecord); + } + } + }); + }); + } + + /** + * 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, + proofAttributes, + }); + const proofId = proof.data.id; + this.log.info("Sent proof request with id", proofId); + + return this.waitForProofCompletionV1(agentName, proofId, timeout); + } +} 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 new file mode 100644 index 0000000000..ec2681c106 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/aries-types.ts @@ -0,0 +1,163 @@ +/** + * Helper functions used mostly to convert from Open API endpoint inputs to Aries compatible structures. + */ + +import { + Agent, + ConnectionsModule, + DidsModule, + CredentialsModule, + V2CredentialProtocol, + ProofsModule, + AutoAcceptProof, + V2ProofProtocol, + AutoAcceptCredential, + Query, + ConnectionRecord, + DidExchangeState, + DidExchangeRole, +} from "@aries-framework/core"; +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 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 by this connector for now. + */ +export type AnoncredAgent = Agent<{ + readonly connections: ConnectionsModule; + readonly credentials: CredentialsModule< + V2CredentialProtocol[] + >; + readonly proofs: ProofsModule< + V2ProofProtocol[] + >; + readonly anoncreds: AnonCredsModule; + readonly anoncredsRs: AnonCredsRsModule; + readonly indyVdr: IndyVdrModule; + readonly dids: DidsModule; + readonly askar: AskarModule; +}>; + +/** + * Convert Cacti OpenAPI input to Aries compatible `AutoAcceptProof` + * + * @param policy `CactiAcceptPolicyV1` + * @returns `AutoAcceptProof` + */ +export function cactiAcceptPolicyToAutoAcceptProof( + policy: CactiAcceptPolicyV1, +): AutoAcceptProof { + switch (policy) { + case CactiAcceptPolicyV1.Always: + return AutoAcceptProof.Always; + case CactiAcceptPolicyV1.ContentApproved: + return AutoAcceptProof.ContentApproved; + case CactiAcceptPolicyV1.Never: + return AutoAcceptProof.Never; + default: + const _unknownPolicy: never = policy; + throw new Error(`Unknown CactiAcceptPolicyV1: ${_unknownPolicy}`); + } +} + +/** + * Convert Cacti OpenAPI input to Aries compatible `AutoAcceptCredential` + * + * @param policy `CactiAcceptPolicyV1` + * @returns `AutoAcceptCredential` + */ +export function cactiAcceptPolicyToAutoAcceptCredential( + policy: CactiAcceptPolicyV1, +): AutoAcceptCredential { + switch (policy) { + case CactiAcceptPolicyV1.Always: + return AutoAcceptCredential.Always; + case CactiAcceptPolicyV1.ContentApproved: + return AutoAcceptCredential.ContentApproved; + case CactiAcceptPolicyV1.Never: + return AutoAcceptCredential.Never; + default: + const _unknownPolicy: never = policy; + throw new Error(`Unknown CactiAcceptPolicyV1: ${_unknownPolicy}`); + } +} + +/** + * 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, +): T[keyof T] | undefined { + if (!value) { + return undefined; + } + + if (!Object.values(enumType).includes(value)) { + throw new Error(`Invalid aries enum value: ${value}`); + } + + 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: 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> { + const attributesArray = proofAttributes.map((attr) => { + return [ + attr.name, + { + name: attr.name, + restrictions: [ + { + [`attr::${attr.name}::value`]: attr.isValueEqual, + cred_def_id: attr.isCredentialDefinitionIdEqual, + }, + ], + }, + ]; + }); + + return Object.fromEntries(attributesArray); +} diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES new file mode 100644 index 0000000000..53250c0269 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES @@ -0,0 +1,5 @@ +api.ts +base.ts +common.ts +configuration.ts +index.ts diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION new file mode 100644 index 0000000000..cd802a1ec4 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.6.0 \ No newline at end of file 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 new file mode 100644 index 0000000000..1e919be9e2 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -0,0 +1,988 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cacti Plugin - Connector Aries + * Can communicate with other Aries agents and Cacti Aries connectors + * + * The version of the OpenAPI document: v2.0.0-alpha.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from './configuration'; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from './common'; +import type { RequestArgs } from './base'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError } from './base'; + +/** + * Request for AcceptInvitation endpoint. + * @export + * @interface AcceptInvitationV1Request + */ +export interface AcceptInvitationV1Request { + /** + * Aries label of an agent to be used to connect using URL + * @type {string} + * @memberof AcceptInvitationV1Request + */ + 'agentName': string; + /** + * Invitation URL generated by another aries agent. + * @type {string} + * @memberof AcceptInvitationV1Request + */ + 'invitationUrl': string; +} +/** + * Response for AcceptInvitation endpoint. + * @export + * @interface AcceptInvitationV1Response + */ +export interface AcceptInvitationV1Response { + /** + * ID that can be used to track status of the connection + * @type {string} + * @memberof AcceptInvitationV1Response + */ + 'outOfBandId': string; +} +/** + * Aries agent connection information. + * @export + * @interface AgentConnectionRecordV1 + */ +export interface AgentConnectionRecordV1 { + [key: string]: any; + + /** + * + * @type {string} + * @memberof AgentConnectionRecordV1 + */ + 'state': string; + /** + * + * @type {string} + * @memberof AgentConnectionRecordV1 + */ + 'role': string; + /** + * + * @type {boolean} + * @memberof AgentConnectionRecordV1 + */ + 'isReady': boolean; + /** + * + * @type {string} + * @memberof AgentConnectionRecordV1 + */ + 'did'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionRecordV1 + */ + 'theirDid'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionRecordV1 + */ + 'theirLabel'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionRecordV1 + */ + 'alias'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionRecordV1 + */ + 'threadId'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionRecordV1 + */ + 'errorMessage'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionRecordV1 + */ + 'outOfBandId'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionRecordV1 + */ + 'invitationDid'?: string; +} +/** + * Fields that can be used to filter agent connection list. + * @export + * @interface AgentConnectionsFilterV1 + */ +export interface AgentConnectionsFilterV1 { + /** + * + * @type {string} + * @memberof AgentConnectionsFilterV1 + */ + 'did'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionsFilterV1 + */ + 'invitationDid'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionsFilterV1 + */ + 'outOfBandId'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionsFilterV1 + */ + 'role'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionsFilterV1 + */ + 'state'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionsFilterV1 + */ + 'theirDid'?: string; + /** + * + * @type {string} + * @memberof AgentConnectionsFilterV1 + */ + 'threadId'?: string; +} +/** + * Aries agent configuration to be setup and used by the connector. + * @export + * @interface AriesAgentConfigV1 + */ +export interface AriesAgentConfigV1 { + /** + * Aries agent label that will also be used as wallet id. + * @type {string} + * @memberof AriesAgentConfigV1 + */ + 'name': string; + /** + * Wallet private key - do not share with anyone. + * @type {string} + * @memberof AriesAgentConfigV1 + */ + 'walletKey': string; + /** + * Path to wallet sqlite database to use. If not provided, the connector default path and agent name will be used. + * @type {string} + * @memberof AriesAgentConfigV1 + */ + 'walletPath'?: string; + /** + * + * @type {Array} + * @memberof AriesAgentConfigV1 + */ + 'indyNetworks': Array; + /** + * Inbound endpoint URL for this agent. Must be unique for this connector. Must contain port. + * @type {string} + * @memberof AriesAgentConfigV1 + */ + 'inboundUrl'?: string; + /** + * Flag to accept new connection by default + * @type {boolean} + * @memberof AriesAgentConfigV1 + */ + 'autoAcceptConnections'?: boolean; + /** + * + * @type {CactiAcceptPolicyV1} + * @memberof AriesAgentConfigV1 + */ + 'autoAcceptCredentials'?: CactiAcceptPolicyV1; + /** + * + * @type {CactiAcceptPolicyV1} + * @memberof AriesAgentConfigV1 + */ + 'autoAcceptProofs'?: CactiAcceptPolicyV1; +} + + +/** + * Summary of an Aries Agent configured in the connector. + * @export + * @interface AriesAgentSummaryV1 + */ +export interface AriesAgentSummaryV1 { + /** + * Aries label of an agent + * @type {string} + * @memberof AriesAgentSummaryV1 + */ + 'name': string; + /** + * True when Aries agent has been initialized properly. + * @type {boolean} + * @memberof AriesAgentSummaryV1 + */ + 'isAgentInitialized': boolean; + /** + * True when this agents wallet has been initialized properly. + * @type {boolean} + * @memberof AriesAgentSummaryV1 + */ + 'isWalletInitialized': boolean; + /** + * True when this agents wallet has been provisioned properly. + * @type {boolean} + * @memberof AriesAgentSummaryV1 + */ + 'isWalletProvisioned': boolean; + /** + * + * @type {AriesAgentSummaryV1WalletConfig} + * @memberof AriesAgentSummaryV1 + */ + 'walletConfig': AriesAgentSummaryV1WalletConfig; + /** + * Aries agent endpoints configured + * @type {Array} + * @memberof AriesAgentSummaryV1 + */ + 'endpoints': Array; +} +/** + * + * @export + * @interface AriesAgentSummaryV1WalletConfig + */ +export interface AriesAgentSummaryV1WalletConfig { + /** + * Wallet entry ID + * @type {string} + * @memberof AriesAgentSummaryV1WalletConfig + */ + 'id': string; + /** + * Wallet storage type + * @type {string} + * @memberof AriesAgentSummaryV1WalletConfig + */ + 'type': string; +} +/** + * Indy VDR network configuration + * @export + * @interface AriesIndyVdrPoolConfigV1 + */ +export interface AriesIndyVdrPoolConfigV1 { + /** + * Indy genesis transactions. + * @type {string} + * @memberof AriesIndyVdrPoolConfigV1 + */ + 'genesisTransactions': string; + /** + * Flag to specify whether this is production or development ledger. + * @type {boolean} + * @memberof AriesIndyVdrPoolConfigV1 + */ + 'isProduction': boolean; + /** + * Indy namespace + * @type {string} + * @memberof AriesIndyVdrPoolConfigV1 + */ + 'indyNamespace': string; + /** + * Connect to the ledger on startup flag + * @type {boolean} + * @memberof AriesIndyVdrPoolConfigV1 + */ + 'connectOnStartup'?: boolean; +} +/** + * Proof exchange record from Aries framework (simplified) + * @export + * @interface AriesProofExchangeRecordV1 + */ +export interface AriesProofExchangeRecordV1 { + [key: string]: any; + + /** + * + * @type {string} + * @memberof AriesProofExchangeRecordV1 + */ + 'id': string; + /** + * + * @type {string} + * @memberof AriesProofExchangeRecordV1 + */ + 'connectionId'?: string; + /** + * + * @type {string} + * @memberof AriesProofExchangeRecordV1 + */ + 'threadId': string; + /** + * + * @type {string} + * @memberof AriesProofExchangeRecordV1 + */ + 'state': string; + /** + * + * @type {string} + * @memberof AriesProofExchangeRecordV1 + */ + 'protocolVersion': string; + /** + * + * @type {boolean} + * @memberof AriesProofExchangeRecordV1 + */ + 'isVerified'?: boolean; + /** + * + * @type {string} + * @memberof AriesProofExchangeRecordV1 + */ + 'errorMessage'?: string; +} +/** + * Credential / Proof requests acceptance policies for Aries agent + * @export + * @enum {string} + */ + +export const CactiAcceptPolicyV1 = { + Always: 'always', + ContentApproved: 'contentApproved', + Never: 'never' +} as const; + +export type CactiAcceptPolicyV1 = typeof CactiAcceptPolicyV1[keyof typeof CactiAcceptPolicyV1]; + + +/** + * Credential attribute checks to be performed by a proof request. + * @export + * @interface CactiProofRequestAttributeV1 + */ +export interface CactiProofRequestAttributeV1 { + /** + * Attribute name. + * @type {string} + * @memberof CactiProofRequestAttributeV1 + */ + 'name': string; + /** + * Check if attribute has specified value + * @type {any} + * @memberof CactiProofRequestAttributeV1 + */ + 'isValueEqual'?: any; + /** + * Check if credentialDefinitionId has specified value + * @type {any} + * @memberof CactiProofRequestAttributeV1 + */ + 'isCredentialDefinitionIdEqual'?: any; +} +/** + * Request for CreateNewConnectionInvitation endpoint. + * @export + * @interface CreateNewConnectionInvitationV1Request + */ +export interface CreateNewConnectionInvitationV1Request { + /** + * Aries label of an agent to use to generate an invitation + * @type {string} + * @memberof CreateNewConnectionInvitationV1Request + */ + 'agentName': string; + /** + * Invitation URL domain to use. If not specified, then connector default domain will be used + * @type {string} + * @memberof CreateNewConnectionInvitationV1Request + */ + 'invitationDomain'?: string; +} +/** + * Response for CreateNewConnectionInvitation endpoint. + * @export + * @interface CreateNewConnectionInvitationV1Response + */ +export interface CreateNewConnectionInvitationV1Response { + /** + * Invitation URL that can be used by another aries agent to connect to us. + * @type {string} + * @memberof CreateNewConnectionInvitationV1Response + */ + 'invitationUrl': string; + /** + * ID that can be used to track status of the connection + * @type {string} + * @memberof CreateNewConnectionInvitationV1Response + */ + 'outOfBandId': string; +} +/** + * Error response from the connector. + * @export + * @interface ErrorExceptionV1Response + */ +export interface ErrorExceptionV1Response { + /** + * Short error description message. + * @type {string} + * @memberof ErrorExceptionV1Response + */ + 'message': string; + /** + * Detailed error information. + * @type {string} + * @memberof ErrorExceptionV1Response + */ + 'error': string; +} +/** + * Request for GetConnections endpoint. + * @export + * @interface GetConnectionsV1Request + */ +export interface GetConnectionsV1Request { + /** + * + * @type {string} + * @memberof GetConnectionsV1Request + */ + 'agentName': string; + /** + * + * @type {AgentConnectionsFilterV1} + * @memberof GetConnectionsV1Request + */ + 'filter'?: AgentConnectionsFilterV1; +} +/** + * Request for RequestProof endpoint. + * @export + * @interface RequestProofV1Request + */ +export interface RequestProofV1Request { + /** + * Aries label of an agent to be used to connect using URL + * @type {string} + * @memberof RequestProofV1Request + */ + 'agentName': string; + /** + * Peer connection ID from which we want to request a proof. + * @type {string} + * @memberof RequestProofV1Request + */ + 'connectionId': string; + /** + * + * @type {Array} + * @memberof RequestProofV1Request + */ + 'proofAttributes': Array; +} +/** + * Options passed when monitoring connection change events. + * @export + * @interface WatchConnectionStateOptionsV1 + */ +export interface WatchConnectionStateOptionsV1 { + /** + * Aries agent label that will also be used as wallet id. + * @type {string} + * @memberof WatchConnectionStateOptionsV1 + */ + 'agentName': string; +} +/** + * Values pushed on each connection state change. + * @export + * @interface WatchConnectionStateProgressV1 + */ +export interface WatchConnectionStateProgressV1 { + /** + * + * @type {AgentConnectionRecordV1} + * @memberof WatchConnectionStateProgressV1 + */ + 'connectionRecord': AgentConnectionRecordV1; + /** + * + * @type {string} + * @memberof WatchConnectionStateProgressV1 + */ + 'previousState': string | null; +} +/** + * Websocket requests for monitoring connection change events. + * @export + * @enum {string} + */ + +export const WatchConnectionStateV1 = { + Subscribe: 'org.hyperledger.cactus.api.async.hlaries.WatchConnectionStateV1.Subscribe', + Next: 'org.hyperledger.cactus.api.async.hlaries.WatchConnectionStateV1.Next', + Unsubscribe: 'org.hyperledger.cactus.api.async.hlaries.WatchConnectionStateV1.Unsubscribe', + Error: 'org.hyperledger.cactus.api.async.hlaries.WatchConnectionStateV1.Error', + Complete: 'org.hyperledger.cactus.api.async.hlaries.WatchConnectionStateV1.Complete' +} as const; + +export type WatchConnectionStateV1 = typeof WatchConnectionStateV1[keyof typeof WatchConnectionStateV1]; + + +/** + * Options passed when monitoring proof state change events. + * @export + * @interface WatchProofStateOptionsV1 + */ +export interface WatchProofStateOptionsV1 { + /** + * Aries agent label that will also be used as wallet id. + * @type {string} + * @memberof WatchProofStateOptionsV1 + */ + 'agentName': string; +} +/** + * Values pushed on each proof state change. + * @export + * @interface WatchProofStateProgressV1 + */ +export interface WatchProofStateProgressV1 { + /** + * + * @type {AriesProofExchangeRecordV1} + * @memberof WatchProofStateProgressV1 + */ + 'proofRecord': AriesProofExchangeRecordV1; + /** + * + * @type {string} + * @memberof WatchProofStateProgressV1 + */ + 'previousState': string | null; +} +/** + * Websocket requests for monitoring proof state change events. + * @export + * @enum {string} + */ + +export const WatchProofStateV1 = { + Subscribe: 'org.hyperledger.cactus.api.async.hlaries.WatchProofStateV1.Subscribe', + Next: 'org.hyperledger.cactus.api.async.hlaries.WatchProofStateV1.Next', + Unsubscribe: 'org.hyperledger.cactus.api.async.hlaries.WatchProofStateV1.Unsubscribe', + Error: 'org.hyperledger.cactus.api.async.hlaries.WatchProofStateV1.Error', + Complete: 'org.hyperledger.cactus.api.async.hlaries.WatchProofStateV1.Complete' +} as const; + +export type WatchProofStateV1 = typeof WatchProofStateV1[keyof typeof WatchProofStateV1]; + + + +/** + * DefaultApi - axios parameter creator + * @export + */ +export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary Connect to another agent using it\'s invitation URL + * @param {AcceptInvitationV1Request} [acceptInvitationV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + acceptInvitationV1: async (acceptInvitationV1Request?: AcceptInvitationV1Request, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/accept-invitation`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(acceptInvitationV1Request, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Create new aries agent invitation that other agents can use to connect. + * @param {CreateNewConnectionInvitationV1Request} [createNewConnectionInvitationV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createNewConnectionInvitationV1: async (createNewConnectionInvitationV1Request?: CreateNewConnectionInvitationV1Request, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/create-new-connection-invitation`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(createNewConnectionInvitationV1Request, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Get all Aries agents configured in this connector plugin. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getAgentsV1: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/get-agents`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Get all connections of given aries agent. + * @param {GetConnectionsV1Request} [getConnectionsV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getConnectionsV1: async (getConnectionsV1Request?: GetConnectionsV1Request, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/get-connections`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(getConnectionsV1Request, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Request proof matching provided requriements from connected peer agent. + * @param {RequestProofV1Request} [requestProofV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + requestProofV1: async (requestProofV1Request?: RequestProofV1Request, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/request-proof`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(requestProofV1Request, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * DefaultApi - functional programming interface + * @export + */ +export const DefaultApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration) + return { + /** + * + * @summary Connect to another agent using it\'s invitation URL + * @param {AcceptInvitationV1Request} [acceptInvitationV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async acceptInvitationV1(acceptInvitationV1Request?: AcceptInvitationV1Request, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.acceptInvitationV1(acceptInvitationV1Request, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Create new aries agent invitation that other agents can use to connect. + * @param {CreateNewConnectionInvitationV1Request} [createNewConnectionInvitationV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createNewConnectionInvitationV1(createNewConnectionInvitationV1Request?: CreateNewConnectionInvitationV1Request, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.createNewConnectionInvitationV1(createNewConnectionInvitationV1Request, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Get all Aries agents configured in this connector plugin. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getAgentsV1(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getAgentsV1(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Get all connections of given aries agent. + * @param {GetConnectionsV1Request} [getConnectionsV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getConnectionsV1(getConnectionsV1Request?: GetConnectionsV1Request, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getConnectionsV1(getConnectionsV1Request, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * + * @summary Request proof matching provided requriements from connected peer agent. + * @param {RequestProofV1Request} [requestProofV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async requestProofV1(requestProofV1Request?: RequestProofV1Request, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.requestProofV1(requestProofV1Request, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + } +}; + +/** + * DefaultApi - factory interface + * @export + */ +export const DefaultApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = DefaultApiFp(configuration) + return { + /** + * + * @summary Connect to another agent using it\'s invitation URL + * @param {AcceptInvitationV1Request} [acceptInvitationV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + acceptInvitationV1(acceptInvitationV1Request?: AcceptInvitationV1Request, options?: any): AxiosPromise { + return localVarFp.acceptInvitationV1(acceptInvitationV1Request, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Create new aries agent invitation that other agents can use to connect. + * @param {CreateNewConnectionInvitationV1Request} [createNewConnectionInvitationV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createNewConnectionInvitationV1(createNewConnectionInvitationV1Request?: CreateNewConnectionInvitationV1Request, options?: any): AxiosPromise { + return localVarFp.createNewConnectionInvitationV1(createNewConnectionInvitationV1Request, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Get all Aries agents configured in this connector plugin. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getAgentsV1(options?: any): AxiosPromise> { + return localVarFp.getAgentsV1(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Get all connections of given aries agent. + * @param {GetConnectionsV1Request} [getConnectionsV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getConnectionsV1(getConnectionsV1Request?: GetConnectionsV1Request, options?: any): AxiosPromise> { + return localVarFp.getConnectionsV1(getConnectionsV1Request, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Request proof matching provided requriements from connected peer agent. + * @param {RequestProofV1Request} [requestProofV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + requestProofV1(requestProofV1Request?: RequestProofV1Request, options?: any): AxiosPromise { + return localVarFp.requestProofV1(requestProofV1Request, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * DefaultApi - object-oriented interface + * @export + * @class DefaultApi + * @extends {BaseAPI} + */ +export class DefaultApi extends BaseAPI { + /** + * + * @summary Connect to another agent using it\'s invitation URL + * @param {AcceptInvitationV1Request} [acceptInvitationV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public acceptInvitationV1(acceptInvitationV1Request?: AcceptInvitationV1Request, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).acceptInvitationV1(acceptInvitationV1Request, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Create new aries agent invitation that other agents can use to connect. + * @param {CreateNewConnectionInvitationV1Request} [createNewConnectionInvitationV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public createNewConnectionInvitationV1(createNewConnectionInvitationV1Request?: CreateNewConnectionInvitationV1Request, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).createNewConnectionInvitationV1(createNewConnectionInvitationV1Request, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Get all Aries agents configured in this connector plugin. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public getAgentsV1(options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).getAgentsV1(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Get all connections of given aries agent. + * @param {GetConnectionsV1Request} [getConnectionsV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public getConnectionsV1(getConnectionsV1Request?: GetConnectionsV1Request, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).getConnectionsV1(getConnectionsV1Request, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Request proof matching provided requriements from connected peer agent. + * @param {RequestProofV1Request} [requestProofV1Request] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public requestProofV1(requestProofV1Request?: RequestProofV1Request, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).requestProofV1(requestProofV1Request, options).then((request) => request(this.axios, this.basePath)); + } +} + + 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 new file mode 100644 index 0000000000..1301b32d8f --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/base.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cacti Plugin - Connector Aries + * Can communicate with other Aries agents and Cacti Aries connectors + * + * The version of the OpenAPI document: v2.0.0-alpha.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from './configuration'; +// Some imports not used depending on template conditions +// @ts-ignore +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; + +export const BASE_PATH = "http://localhost".replace(/\/+$/, ""); + +/** + * + * @export + */ +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +/** + * + * @export + * @interface RequestArgs + */ +export interface RequestArgs { + url: string; + options: AxiosRequestConfig; +} + +/** + * + * @export + * @class BaseAPI + */ +export class BaseAPI { + protected configuration: Configuration | undefined; + + constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath || this.basePath; + } + } +}; + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + constructor(public field: string, msg?: string) { + super(msg); + this.name = "RequiredError" + } +} 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 new file mode 100644 index 0000000000..42c73a1d67 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/common.ts @@ -0,0 +1,150 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cacti Plugin - Connector Aries + * Can communicate with other Aries agents and Cacti Aries connectors + * + * The version of the OpenAPI document: v2.0.0-alpha.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from "./configuration"; +import type { RequestArgs } from "./base"; +import type { AxiosInstance, AxiosResponse } from 'axios'; +import { RequiredError } from "./base"; + +/** + * + * @export + */ +export const DUMMY_BASE_URL = 'https://example.com' + +/** + * + * @throws {RequiredError} + * @export + */ +export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) { + if (paramValue === null || paramValue === undefined) { + throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`); + } +} + +/** + * + * @export + */ +export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) { + if (configuration && configuration.apiKey) { + const localVarApiKeyValue = typeof configuration.apiKey === 'function' + ? await configuration.apiKey(keyParamName) + : await configuration.apiKey; + object[keyParamName] = localVarApiKeyValue; + } +} + +/** + * + * @export + */ +export const setBasicAuthToObject = function (object: any, configuration?: Configuration) { + if (configuration && (configuration.username || configuration.password)) { + object["auth"] = { username: configuration.username, password: configuration.password }; + } +} + +/** + * + * @export + */ +export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + object["Authorization"] = "Bearer " + accessToken; + } +} + +/** + * + * @export + */ +export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const localVarAccessTokenValue = typeof configuration.accessToken === 'function' + ? await configuration.accessToken(name, scopes) + : await configuration.accessToken; + object["Authorization"] = "Bearer " + localVarAccessTokenValue; + } +} + +function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void { + if (parameter == null) return; + if (typeof parameter === "object") { + if (Array.isArray(parameter)) { + (parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key)); + } + else { + Object.keys(parameter).forEach(currentKey => + setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`) + ); + } + } + else { + if (urlSearchParams.has(key)) { + urlSearchParams.append(key, parameter); + } + else { + urlSearchParams.set(key, parameter); + } + } +} + +/** + * + * @export + */ +export const setSearchParams = function (url: URL, ...objects: any[]) { + const searchParams = new URLSearchParams(url.search); + setFlattenedQueryParams(searchParams, objects); + url.search = searchParams.toString(); +} + +/** + * + * @export + */ +export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) { + const nonString = typeof value !== 'string'; + const needsSerialization = nonString && configuration && configuration.isJsonMime + ? configuration.isJsonMime(requestOptions.headers['Content-Type']) + : nonString; + return needsSerialization + ? JSON.stringify(value !== undefined ? value : {}) + : (value || ""); +} + +/** + * + * @export + */ +export const toPathString = function (url: URL) { + return url.pathname + url.search + url.hash +} + +/** + * + * @export + */ +export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) { + return >(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs = {...axiosArgs.options, url: (configuration?.basePath || basePath) + axiosArgs.url}; + return axios.request(axiosRequestArgs); + }; +} 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 new file mode 100644 index 0000000000..21a36f4aa5 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/configuration.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cacti Plugin - Connector Aries + * Can communicate with other Aries agents and Cacti Aries connectors + * + * The version of the OpenAPI document: v2.0.0-alpha.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface ConfigurationParameters { + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + username?: string; + password?: string; + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + basePath?: string; + baseOptions?: any; + formDataCtor?: new () => any; +} + +export class Configuration { + /** + * parameter for apiKey security + * @param name security name + * @memberof Configuration + */ + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + username?: string; + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + password?: string; + /** + * parameter for oauth2 security + * @param name security name + * @param scopes oauth2 scope + * @memberof Configuration + */ + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + /** + * override base path + * + * @type {string} + * @memberof Configuration + */ + basePath?: string; + /** + * base options for axios calls + * + * @type {any} + * @memberof Configuration + */ + baseOptions?: any; + /** + * The FormData constructor that will be used to create multipart form data + * requests. You can inject this here so that execution environments that + * do not support the FormData class can still run the generated client. + * + * @type {new () => FormData} + */ + formDataCtor?: new () => any; + + constructor(param: ConfigurationParameters = {}) { + this.apiKey = param.apiKey; + this.username = param.username; + this.password = param.password; + this.accessToken = param.accessToken; + this.basePath = param.basePath; + this.baseOptions = param.baseOptions; + this.formDataCtor = param.formDataCtor; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); + return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); + } +} 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 new file mode 100644 index 0000000000..c6f463742d --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/generated/openapi/typescript-axios/index.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cacti Plugin - Connector Aries + * Can communicate with other Aries agents and Cacti Aries connectors + * + * The version of the OpenAPI document: v2.0.0-alpha.2 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export * from "./api"; +export * from "./configuration"; + diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/index.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/index.ts new file mode 100755 index 0000000000..87cb558397 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/index.ts @@ -0,0 +1 @@ +export * from "./public-api"; diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/index.web.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/index.web.ts new file mode 100755 index 0000000000..1c773242d5 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/index.web.ts @@ -0,0 +1 @@ +export * from "./generated/openapi/typescript-axios"; diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/plugin-factory-ledger-connector.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/plugin-factory-ledger-connector.ts new file mode 100644 index 0000000000..3a3f0cc94c --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/plugin-factory-ledger-connector.ts @@ -0,0 +1,20 @@ +import { + IPluginFactoryOptions, + PluginFactory, +} from "@hyperledger/cactus-core-api"; +import { + IPluginLedgerConnectorAriesOptions, + PluginLedgerConnectorAries, +} from "./plugin-ledger-connector-aries"; + +export class PluginFactoryLedgerConnector extends PluginFactory< + PluginLedgerConnectorAries, + IPluginLedgerConnectorAriesOptions, + IPluginFactoryOptions +> { + async create( + pluginOptions: IPluginLedgerConnectorAriesOptions, + ): Promise { + return new PluginLedgerConnectorAries(pluginOptions); + } +} 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 new file mode 100644 index 0000000000..5836337ef2 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/plugin-ledger-connector-aries.ts @@ -0,0 +1,672 @@ +import type { + Server as SocketIoServer, + Socket as SocketIoSocket, +} from "socket.io"; +import type { Express } from "express"; +import * as path from "node:path"; +import * as os from "node:os"; +import { AskarModule } from "@aries-framework/askar"; +import { + Agent, + InitConfig, + HttpOutboundTransport, + ConnectionsModule, + DidsModule, + TypedArrayEncoder, + KeyType, + CredentialsModule, + V2CredentialProtocol, + ProofsModule, + AutoAcceptProof, + V2ProofProtocol, + AutoAcceptCredential, +} 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 { + AnonCredsCredentialFormatService, + AnonCredsModule, + AnonCredsProofFormatService, +} 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 { + 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, + 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 { RequestProofEndpoint } 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"; +const DEFAULT_WALLET_PATH = path.join( + os.homedir(), + ".cacti/cactus-plugin-ledger-connector-aries/wallet", +); + +export interface IPluginLedgerConnectorAriesOptions + extends ICactusPluginOptions { + logLevel?: LogLevelDesc; + // pluginRegistry: PluginRegistry; + ariesAgents?: AriesAgentConfigV1[]; + invitationDomain?: string; + walletPath?: string; +} + +export class PluginLedgerConnectorAries + implements ICactusPlugin, IPluginWebService +{ + // private readonly pluginRegistry: PluginRegistry; + private readonly instanceId: string; + private readonly invitationDomain: string; + private readonly walletPath: string; + private readonly log: Logger; + private endpoints: IWebServiceEndpoint[] | undefined; + private ariesAgentConfigs: AriesAgentConfigV1[] | undefined; + private ariesAgents = new Map(); + private connectedSockets = new Map(); + + public get className(): string { + return "PluginLedgerConnectorAries"; + } + + constructor(public readonly options: IPluginLedgerConnectorAriesOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy(options.instanceId, `${fnTag} options.instanceId`); + // Checks.truthy(options.pluginRegistry, `${fnTag} options.pluginRegistry`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.instanceId = options.instanceId; + this.invitationDomain = + options.invitationDomain ?? DEFAULT_INVITATION_DOMAIN; + this.walletPath = options.walletPath ?? DEFAULT_WALLET_PATH; + this.ariesAgentConfigs = options.ariesAgents; + // this.pluginRegistry = options.pluginRegistry as PluginRegistry; + } + + public getOpenApiSpec(): unknown { + return OAS; + } + + public getInstanceId(): string { + 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}...`); + + this.log.debug("Disconnect all the sockets"); + this.connectedSockets.forEach((socket) => socket.disconnect()); + + for (const [agentName, agent] of this.ariesAgents) { + this.log.debug("Shutdown agent", agentName); + await agent.shutdown(); + } + + this.ariesAgents.clear(); + } + + /** + * 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); + } + } + } + + async registerWebServices( + app: Express, + 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); + + // WatchConnectionStateV1 + socket.on( + WatchConnectionStateV1.Subscribe, + async (options: WatchConnectionStateOptionsV1) => { + try { + const agent = await this.getAriesAgentOrThrow(options.agentName); + new WatchConnectionStateV1Endpoint({ + socket, + logLevel, + agent, + }).subscribe(); + } catch (error) { + this.log.warn(WatchConnectionStateV1.Error, error); + socket.emit( + WatchConnectionStateV1.Error, + safeStringifyException(error), + ); + socket.disconnect(); + } + }, + ); + + // WatchProofStateV1Endpoint + socket.on( + WatchProofStateV1.Subscribe, + async (options: WatchProofStateOptionsV1) => { + try { + const agent = await this.getAriesAgentOrThrow(options.agentName); + new WatchProofStateV1Endpoint({ + socket, + logLevel, + agent, + }).subscribe(); + } catch (error) { + this.log.warn(WatchProofStateV1.Error, error); + socket.emit(WatchProofStateV1.Error, safeStringifyException(error)); + socket.disconnect(); + } + }, + ); + + // Disconnect + socket.on("disconnect", () => { + this.connectedSockets.delete(socket.id); + }); + }); + + return webServices; + } + + public async getOrCreateWebServices(): Promise { + if (Array.isArray(this.endpoints)) { + return this.endpoints; + } + + const endpoints: IWebServiceEndpoint[] = []; + { + const endpoint = new GetAgentsEndpoint({ + connector: this, + logLevel: this.options.logLevel, + }); + endpoints.push(endpoint); + } + { + const endpoint = new RequestProofEndpoint({ + connector: this, + logLevel: this.options.logLevel, + }); + endpoints.push(endpoint); + } + { + const endpoint = new GetConnectionsEndpoint({ + connector: this, + logLevel: this.options.logLevel, + }); + endpoints.push(endpoint); + } + { + const endpoint = new CreateNewConnectionInvitationEndpoint({ + connector: this, + logLevel: this.options.logLevel, + }); + endpoints.push(endpoint); + } + { + const endpoint = new AcceptInvitationEndpoint({ + connector: this, + logLevel: this.options.logLevel, + }); + endpoints.push(endpoint); + } + + this.endpoints = endpoints; + return endpoints; + } + + public getPackageName(): string { + 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) { + this.log.error( + `Agent ${agent.config.label} doesn't have a wallet configured!`, + ); + } + + return { + name: agent.config.label, + isAgentInitialized: agent.isInitialized, + isWalletInitialized: agent.wallet.isInitialized, + isWalletProvisioned: agent.wallet.isProvisioned, + walletConfig: { + 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:`, + // autoAcceptConnections, + // ); + // const autoAcceptCredentials = agentConfig.autoAcceptCredentials + // ? cactiAcceptPolicyToAutoAcceptCredential( + // agentConfig.autoAcceptCredentials, + // ) + // : AutoAcceptCredential.Never; + // this.log.debug( + // `Agent ${agentConfig.name} autoAcceptCredentials:`, + // autoAcceptCredentials, + // ); + // const autoAcceptProofs = agentConfig.autoAcceptProofs + // ? cactiAcceptPolicyToAutoAcceptProof(agentConfig.autoAcceptProofs) + // : AutoAcceptProof.Never; + // this.log.debug( + // `Agent ${agentConfig.name} autoAcceptProofs:`, + // autoAcceptProofs, + // ); + + const autoAcceptConnections = true; + this.log.debug( + `Agent ${agentConfig.name} autoAcceptConnections:`, + autoAcceptConnections, + ); + const autoAcceptCredentials = AutoAcceptCredential.ContentApproved; + this.log.debug( + `Agent ${agentConfig.name} autoAcceptCredentials:`, + autoAcceptCredentials, + ); + const autoAcceptProofs = AutoAcceptProof.ContentApproved; + this.log.debug( + `Agent ${agentConfig.name} autoAcceptProofs:`, + autoAcceptProofs, + ); + + return { + connections: new ConnectionsModule({ + autoAcceptConnections, + }), + + credentials: new CredentialsModule({ + autoAcceptCredentials, + credentialProtocols: [ + new V2CredentialProtocol({ + credentialFormats: [new AnonCredsCredentialFormatService()], + }), + ], + }), + + proofs: new ProofsModule({ + autoAcceptProofs, + proofProtocols: [ + new V2ProofProtocol({ + proofFormats: [new AnonCredsProofFormatService()], + }), + ], + }), + + anoncreds: new AnonCredsModule({ + registries: [new IndyVdrAnonCredsRegistry()], + }), + + anoncredsRs: new AnonCredsRsModule({ + anoncreds, + }), + + indyVdr: new IndyVdrModule({ + indyVdr, + networks: agentConfig.indyNetworks as [ + IndyVdrPoolConfig, + ...IndyVdrPoolConfig[], + ], + }), + + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrIndyDidResolver()], + }), + + askar: new AskarModule({ ariesAskar }), + } 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`, + ); + Checks.truthy( + agentConfig.indyNetworks, + `addAriesAgent arg agentConfig.indyNetworks`, + ); + + let walletPath = path.join(this.walletPath, `${agentConfig.name}.sqlite`); + if (agentConfig.walletPath) { + walletPath = agentConfig.walletPath; + } + const config: InitConfig = { + label: agentConfig.name, + walletConfig: { + id: agentConfig.name, + key: agentConfig.walletKey, + storage: { + type: "sqlite", + path: walletPath, + }, + }, + endpoints: agentConfig.inboundUrl ? [agentConfig.inboundUrl] : undefined, + }; + + const agent = new Agent({ + config, + modules: this.getAskarAnonCredsIndyModules(agentConfig), + dependencies: agentDependencies, + }); + + if (agentConfig.inboundUrl) { + const port = parseInt(new URL(agentConfig.inboundUrl).port, 10); + if (!port) { + throw new Error( + `inboundUrl (${agentConfig.inboundUrl}) for agent ${agentConfig.name} must contain port`, + ); + } + agent.registerInboundTransport(new HttpInboundTransport({ port })); + } + agent.registerOutboundTransport(new HttpOutboundTransport()); + + 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); + this.log.info("removeAriesAgent(): Agent removed:", agentName); + } else { + this.log.warn( + "removeAriesAgent(): No agent to remove with a name", + agentName, + ); + } + } + + /** + * 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); + const key = await agent.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey: seedBuffer, + }); + + // did is first 16 bytes of public key encoded as base58 + const unqualifiedIndyDid = TypedArrayEncoder.toBase58( + key.publicKey.slice(0, 16), + ); + + const did = `did:indy:${indyNamespace}:${unqualifiedIndyDid}`; + + await agent.dids.import({ + did, + }); + + return did; + } + + /** + * 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(); + + return { + invitationUrl: outOfBandRecord.outOfBandInvitation.toUrl({ + domain: invitationDomain ?? this.invitationDomain, + }), + outOfBandId: outOfBandRecord.id, + }; + } + + /** + * 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 } = + await agent.oob.receiveInvitationFromUrl(invitationUrl); + + return { + outOfBandId: outOfBandRecord.id, + }; + } + + /** + * 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, "requestProof agentName options"); + Checks.truthy(connectionId, "requestProof connectionId options"); + Checks.truthy(proofAttributes, "requestProof proofAttributes options"); + Checks.truthy( + proofAttributes.length > 0, + "requestProof proofAttributes - must be at least one", + ); + const agent = await this.getAriesAgentOrThrow(agentName); + + const proof = await agent.proofs.requestProof({ + protocolVersion: "v2", + connectionId, + proofFormats: { + anoncreds: { + name: "proof-request", + version: "1.0", + requested_attributes: + await cactiAttributesToAnonCredsRequestedAttributes( + proofAttributes, + ), + }, + }, + }); + + return proof; + } +} diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/public-api.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/public-api.ts new file mode 100755 index 0000000000..c09a28308b --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/public-api.ts @@ -0,0 +1,24 @@ +export * from "./generated/openapi/typescript-axios"; + +export { + PluginLedgerConnectorAries, + IPluginLedgerConnectorAriesOptions, +} from "./plugin-ledger-connector-aries"; + +export { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector"; + +import { IPluginFactoryOptions } from "@hyperledger/cactus-core-api"; +import { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector"; + +export { + AriesApiClient, + AriesApiClientOptions, +} from "./api-client/aries-api-client"; + +export * from "./generated/openapi/typescript-axios/api"; + +export async function createPluginFactory( + pluginFactoryOptions: IPluginFactoryOptions, +): Promise { + return new PluginFactoryLedgerConnector(pluginFactoryOptions); +} diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/accept-invitation-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/accept-invitation-v1-endpoint.ts new file mode 100644 index 0000000000..123d8edc18 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/accept-invitation-v1-endpoint.ts @@ -0,0 +1,103 @@ +import type { Express, Request, Response } from "express"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, + safeStringifyException, +} from "@hyperledger/cactus-common"; +import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorAries } from "../plugin-ledger-connector-aries"; +import OAS from "../../json/openapi.json"; + +export interface IAcceptInvitationOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorAries; +} + +export class AcceptInvitationEndpoint implements IWebServiceEndpoint { + private readonly log: Logger; + + public get className(): string { + return "AcceptInvitationEndpoint"; + } + + constructor(public readonly options: IAcceptInvitationOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy(options.connector, `${fnTag} arg options.connector`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/accept-invitation"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/accept-invitation" + ]; + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public async handleRequest(req: Request, res: Response): Promise { + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + try { + res + .status(200) + .json( + await this.options.connector.acceptInvitation( + req.body.agentName, + req.body.invitationUrl, + ), + ); + } catch (ex) { + this.log.error(`Crash while serving ${reqTag}`, ex); + + res.status(500).json({ + message: "Internal Server Error", + error: safeStringifyException(ex), + }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/create-new-connection-invitation-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/create-new-connection-invitation-v1-endpoint.ts new file mode 100644 index 0000000000..f2805929bf --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/create-new-connection-invitation-v1-endpoint.ts @@ -0,0 +1,105 @@ +import type { Express, Request, Response } from "express"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, + safeStringifyException, +} from "@hyperledger/cactus-common"; +import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorAries } from "../plugin-ledger-connector-aries"; +import OAS from "../../json/openapi.json"; + +export interface ICreateNewConnectionInvitationOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorAries; +} + +export class CreateNewConnectionInvitationEndpoint + implements IWebServiceEndpoint +{ + private readonly log: Logger; + + public get className(): string { + return "CreateNewConnectionInvitationEndpoint"; + } + + constructor(public readonly options: ICreateNewConnectionInvitationOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy(options.connector, `${fnTag} arg options.connector`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/create-new-connection-invitation"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/create-new-connection-invitation" + ]; + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public async handleRequest(req: Request, res: Response): Promise { + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + try { + res + .status(200) + .json( + await this.options.connector.createNewConnectionInvitation( + req.body.agentName, + req.body.invitationDomain, + ), + ); + } catch (ex) { + this.log.error(`Crash while serving ${reqTag}`, ex); + + res.status(500).json({ + message: "Internal Server Error", + error: safeStringifyException(ex), + }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/get-agents-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/get-agents-v1-endpoint.ts new file mode 100644 index 0000000000..5d896b8354 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/get-agents-v1-endpoint.ts @@ -0,0 +1,96 @@ +import type { Express, Request, Response } from "express"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, + safeStringifyException, +} from "@hyperledger/cactus-common"; +import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorAries } from "../plugin-ledger-connector-aries"; +import OAS from "../../json/openapi.json"; + +export interface IGetAgentsOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorAries; +} + +export class GetAgentsEndpoint implements IWebServiceEndpoint { + private readonly log: Logger; + + public get className(): string { + return "GetAgentsEndpoint"; + } + + constructor(public readonly options: IGetAgentsOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy(options.connector, `${fnTag} arg options.connector`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/get-agents"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/get-agents" + ]; + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public async handleRequest(req: Request, res: Response): Promise { + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + try { + res.status(200).json(await this.options.connector.getAgents()); + } catch (ex) { + this.log.error(`Crash while serving ${reqTag}`, ex); + + res.status(500).json({ + message: "Internal Server Error", + error: safeStringifyException(ex), + }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/get-connections-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/get-connections-v1-endpoint.ts new file mode 100644 index 0000000000..bd23474054 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/get-connections-v1-endpoint.ts @@ -0,0 +1,103 @@ +import type { Express, Request, Response } from "express"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, + safeStringifyException, +} from "@hyperledger/cactus-common"; +import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorAries } from "../plugin-ledger-connector-aries"; +import OAS from "../../json/openapi.json"; + +export interface IGetConnectionsOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorAries; +} + +export class GetConnectionsEndpoint implements IWebServiceEndpoint { + private readonly log: Logger; + + public get className(): string { + return "GetConnectionsEndpoint"; + } + + constructor(public readonly options: IGetConnectionsOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy(options.connector, `${fnTag} arg options.connector`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/get-connections"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/get-connections" + ]; + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public async handleRequest(req: Request, res: Response): Promise { + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + try { + res + .status(200) + .json( + await this.options.connector.getConnections( + req.body.agentName, + req.body.filter, + ), + ); + } catch (ex) { + this.log.error(`Crash while serving ${reqTag}`, ex); + + res.status(500).json({ + message: "Internal Server Error", + error: safeStringifyException(ex), + }); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/request-proof-v1-endpoint.ts b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/request-proof-v1-endpoint.ts new file mode 100644 index 0000000000..948e894ec2 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/request-proof-v1-endpoint.ts @@ -0,0 +1,104 @@ +import type { Express, Request, Response } from "express"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, + safeStringifyException, +} from "@hyperledger/cactus-common"; +import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorAries } from "../plugin-ledger-connector-aries"; +import OAS from "../../json/openapi.json"; + +export interface IRequestProofOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorAries; +} + +export class RequestProofEndpoint implements IWebServiceEndpoint { + private readonly log: Logger; + + public get className(): string { + return "RequestProofEndpoint"; + } + + constructor(public readonly options: IRequestProofOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy(options.connector, `${fnTag} arg options.connector`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/request-proof"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-aries/request-proof" + ]; + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public async handleRequest(req: Request, res: Response): Promise { + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + try { + res + .status(200) + .json( + await this.options.connector.requestProof( + req.body.agentName, + req.body.connectionId, + req.body.proofAttributes, + ), + ); + } catch (ex) { + this.log.error(`Crash while serving ${reqTag}`, ex); + + res.status(500).json({ + message: "Internal Server Error", + error: safeStringifyException(ex), + }); + } + } +} 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 new file mode 100644 index 0000000000..0cb0a718b6 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/watch-connection-state-v1-endpoint.ts @@ -0,0 +1,89 @@ +import { Socket as SocketIoSocket } from "socket.io"; +import { + ConnectionEventTypes, + ConnectionStateChangedEvent, +} from "@aries-framework/core"; + +import { + Logger, + LogLevelDesc, + LoggerProvider, + Checks, +} from "@hyperledger/cactus-common"; + +import { + WatchConnectionStateProgressV1, + WatchConnectionStateV1, +} from "../generated/openapi/typescript-axios"; +import { AnoncredAgent } from "../aries-types"; + +export interface IWatchConnectionStateV1EndpointConfiguration { + logLevel?: LogLevelDesc; + socket: SocketIoSocket; + agent: AnoncredAgent; +} + +export class WatchConnectionStateV1Endpoint { + private readonly log: Logger; + private readonly socket: SocketIoSocket< + Record void>, + Record< + WatchConnectionStateV1, + (next: WatchConnectionStateProgressV1) => void + > + >; + private readonly agent: AnoncredAgent; + + public get className(): string { + return "WatchConnectionStateV1Endpoint"; + } + + constructor( + public readonly config: IWatchConnectionStateV1EndpointConfiguration, + ) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(config, `${fnTag} arg options`); + Checks.truthy(config.socket, `${fnTag} arg options.socket`); + + const level = this.config.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.socket = config.socket; + this.agent = config.agent; + } + + public async subscribe(): Promise { + const { socket, log, agent } = this; + log.info(`${WatchConnectionStateV1.Subscribe} => ${socket.id}`); + + const eventListener = (e: ConnectionStateChangedEvent) => { + socket.emit(WatchConnectionStateV1.Next, e.payload); + }; + + agent.events.on( + ConnectionEventTypes.ConnectionStateChanged, + eventListener, + ); + + socket.on("disconnect", async (reason: string) => { + log.info("WebSocket:disconnect reason=%o", reason); + agent.events.off( + ConnectionEventTypes.ConnectionStateChanged, + eventListener, + ); + }); + + socket.on(WatchConnectionStateV1.Unsubscribe, async () => { + agent.events.off( + ConnectionEventTypes.ConnectionStateChanged, + eventListener, + ); + log.debug("WatchConnectionStateV1 unsubscribe done."); + }); + + log.debug( + `Subscribing to connection state changes on aries agent ${agent.config.label}...`, + ); + } +} 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 new file mode 100644 index 0000000000..d0f1a27f94 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/main/typescript/web-services/watch-proof-state-v1-endpoint.ts @@ -0,0 +1,81 @@ +import { Socket as SocketIoSocket } from "socket.io"; +import { ProofStateChangedEvent, ProofEventTypes } from "@aries-framework/core"; + +import { + Logger, + LogLevelDesc, + LoggerProvider, + Checks, +} from "@hyperledger/cactus-common"; + +import { + WatchProofStateProgressV1, + WatchProofStateV1, +} from "../generated/openapi/typescript-axios"; +import { AnoncredAgent } from "../aries-types"; + +export interface IWatchProofStateV1EndpointConfiguration { + logLevel?: LogLevelDesc; + socket: SocketIoSocket; + agent: AnoncredAgent; +} + +export class WatchProofStateV1Endpoint { + private readonly log: Logger; + private readonly socket: SocketIoSocket< + Record void>, + Record void> + >; + private readonly agent: AnoncredAgent; + + public get className(): string { + return "WatchProofStateV1Endpoint"; + } + + constructor(public readonly config: IWatchProofStateV1EndpointConfiguration) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(config, `${fnTag} arg options`); + Checks.truthy(config.socket, `${fnTag} arg options.socket`); + + const level = this.config.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.socket = config.socket; + this.agent = config.agent; + } + + public async subscribe(): Promise { + const { socket, log, agent } = this; + log.info(`${WatchProofStateV1.Subscribe} => ${socket.id}`); + + const eventListener = (e: ProofStateChangedEvent) => { + socket.emit(WatchProofStateV1.Next, e.payload); + }; + + agent.events.on( + ProofEventTypes.ProofStateChanged, + eventListener, + ); + + socket.on("disconnect", async (reason: string) => { + log.info("WebSocket:disconnect reason=%o", reason); + agent.events.off( + ProofEventTypes.ProofStateChanged, + eventListener, + ); + }); + + socket.on(WatchProofStateV1.Unsubscribe, async () => { + agent.events.off( + ProofEventTypes.ProofStateChanged, + eventListener, + ); + log.debug("WatchProofStateV1 unsubscribe done."); + }); + + log.debug( + `Subscribing to proof state changes on aries agent ${agent.config.label}...`, + ); + } +} diff --git a/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/api-surface.test.ts b/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/api-surface.test.ts new file mode 100644 index 0000000000..a65ea58247 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/api-surface.test.ts @@ -0,0 +1,5 @@ +import * as apiSurface from "../../../main/typescript/public-api"; + +test("Library can be loaded", async () => { + expect(apiSurface).toBeTruthy(); +}); diff --git a/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/aries-setup-and-connections.test.ts b/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/aries-setup-and-connections.test.ts new file mode 100644 index 0000000000..887ddf62d2 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/integration/aries-setup-and-connections.test.ts @@ -0,0 +1,425 @@ +////////////////////////////////// +// Constants +////////////////////////////////// + +// Ledger settings +const containerImageName = "ghcr.io/outsh/cactus-indy-all-in-one"; +const containerImageVersion = "0.1"; + +// 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 = "info"; + +import "jest-extended"; +import express from "express"; +import bodyParser from "body-parser"; +import http from "http"; +import { v4 as uuidV4 } from "uuid"; +import { AddressInfo } from "net"; +import { Server as SocketIoServer } from "socket.io"; + +import { + LogLevelDesc, + LoggerProvider, + Logger, + Servers, +} from "@hyperledger/cactus-common"; +import { Configuration, Constants } from "@hyperledger/cactus-core-api"; +import { + IndyTestLedger, + pruneDockerAllIfGithubAction, +} from "@hyperledger/cactus-test-tooling"; +import * as path from "node:path"; +import * as os from "node:os"; +import { rm } from "node:fs/promises"; + +import { + PluginLedgerConnectorAries, + AriesApiClient, + AriesAgentSummaryV1, + AgentConnectionRecordV1, +} from "../../../main/typescript/public-api"; + +// Logger setup +const log: Logger = LoggerProvider.getOrCreate({ + label: "aries-setup-and-connections.test", + level: testLogLevel, +}); + +const testWalletPath = path.join( + os.tmpdir(), + "aries-setup-and-connections-test-wallet", +); + +////////////////////////////////// +// Setup Tests +////////////////////////////////// + +describe("Aries connector setup tests", () => { + const fakeIndyNetworkConfig = { + isProduction: false, + genesisTransactions: JSON.stringify({ + reqSignature: {}, + txn: { + data: { + data: { + alias: "Node1", + client_ip: "172.16.0.2", + client_port: 9702, + node_ip: "172.16.0.2", + node_port: 9701, + services: ["VALIDATOR"], + }, + dest: "foo", + }, + metadata: { from: "foo" }, + type: "0", + }, + txnMetadata: { + seqNo: 1, + txnId: + "fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62", + }, + ver: "1", + }), + indyNamespace: "cacti:test", + connectOnStartup: true, + }; + let connector: PluginLedgerConnectorAries; + + beforeEach(() => { + connector = new PluginLedgerConnectorAries({ + instanceId: uuidV4(), + logLevel: testLogLevel, + walletPath: testWalletPath, + }); + }); + + afterEach(async () => { + if (connector) { + log.info("Cleanup the temporary connector..."); + await connector.shutdown(); + } + }); + + afterAll(async () => { + try { + await rm(testWalletPath, { + recursive: true, + force: true, + maxRetries: 5, + }); + log.info(`${testWalletPath} removed successfully.`); + } catch (error) { + log.warn(`${testWalletPath} could not be removed:`, error); + } + }); + + test("Basic connector construction works", async () => { + expect(connector).toBeTruthy(); + const allAgents = await connector.getAgents(); + expect(allAgents.length).toBe(0); + }); + + test("Adding simple aries agent works", async () => { + const agentName = `simple-${uuidV4()}`; + + await connector.addAriesAgent({ + name: agentName, + walletKey: agentName, + indyNetworks: [fakeIndyNetworkConfig], + }); + + const allAgents = await connector.getAgents(); + expect(allAgents.length).toBe(1); + const agent = allAgents.pop() as AriesAgentSummaryV1; + expect(agent).toBeTruthy(); + expect(agent.name).toEqual(agentName); + expect(agent.walletConfig.id).toEqual(agentName); + expect(agent.isAgentInitialized).toBeTrue(); + expect(agent.isWalletInitialized).toBeTrue(); + expect(agent.isWalletProvisioned).toBeTrue(); + }); + + test("Adding aries agent with inbound url works", async () => { + const agentName = `inbound-${uuidV4()}`; + const inboundUrl = "http://127.0.0.1:8253"; + + await connector.addAriesAgent({ + name: agentName, + walletKey: agentName, + indyNetworks: [fakeIndyNetworkConfig], + inboundUrl, + }); + + const allAgents = await connector.getAgents(); + expect(allAgents.length).toBe(1); + const agent = allAgents.pop() as AriesAgentSummaryV1; + expect(agent).toBeTruthy(); + expect(agent.name).toEqual(agentName); + expect(agent.walletConfig.id).toEqual(agentName); + expect(agent.isAgentInitialized).toBeTrue(); + expect(agent.isWalletInitialized).toBeTrue(); + expect(agent.isWalletProvisioned).toBeTrue(); + + // Check endpoint + expect(agent.endpoints.length).toBe(1); + const agentEndpoint = agent.endpoints.pop() as string; + expect(agentEndpoint).toBeTruthy(); + expect(agentEndpoint).toEqual(inboundUrl); + }); + + test("Adding aries agent with invalid inbound url throws error", async () => { + const agentName = `shouldThrow-${uuidV4()}`; + + try { + await connector.addAriesAgent({ + name: agentName, + walletKey: agentName, + indyNetworks: [fakeIndyNetworkConfig], + inboundUrl: "foo", + }); + expect("should throw!").toBe(0); + } catch (error) { + log.info( + "Adding aries agent with inbound url 'foo' throws error as expected", + ); + } + + try { + await connector.addAriesAgent({ + name: agentName, + walletKey: agentName, + indyNetworks: [fakeIndyNetworkConfig], + inboundUrl: "http://127.0.0.1", + }); + expect("should throw!").toBe(0); + } catch (error) { + log.info( + "Adding aries agent without inbound url port throws error as expected", + ); + } + + const allAgents = await connector.getAgents(); + expect(allAgents.length).toBe(0); + }); + + test("Removing single aries agent works", async () => { + const agentName = `removeCheck-${uuidV4()}`; + + await connector.addAriesAgent({ + name: agentName, + walletKey: agentName, + indyNetworks: [fakeIndyNetworkConfig], + }); + + const allAgents = await connector.getAgents(); + expect(allAgents.length).toBe(1); + await connector.removeAriesAgent(agentName); + const allAgentsAfterRemoval = await connector.getAgents(); + expect(allAgentsAfterRemoval.length).toBe(0); + }); + + test("Connector shutdown clears aries agent", async () => { + const agentName = `shutdownCheck-${uuidV4()}`; + + await connector.addAriesAgent({ + name: agentName, + walletKey: agentName, + indyNetworks: [fakeIndyNetworkConfig], + }); + + const allAgents = await connector.getAgents(); + expect(allAgents.length).toBe(1); + await connector.shutdown(); + const allAgentsAfterShutdown = await connector.getAgents(); + expect(allAgentsAfterShutdown.length).toBe(0); + }); +}); + +////////////////////////////////// +// Connect Tests +////////////////////////////////// + +describe("Connect Aries agents through connector tests", () => { + let addressInfo, + address: string, + port: number, + apiHost, + apiConfig, + ledger: IndyTestLedger, + apiClient: AriesApiClient, + connector: PluginLedgerConnectorAries; + + const indyNamespace = "cacti:test"; + const aliceAgentName = `cacti-alice-${uuidV4()}`; + const aliceInboundUrl = "http://127.0.0.1:8255"; + const bobAgentName = `cacti-bob-${uuidV4()}`; + const bobInboundUrl = "http://127.0.0.1:8256"; + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + const server = http.createServer(expressApp); + const wsApi = new SocketIoServer(server, { + path: Constants.SocketIoConnectionPathV1, + }); + + beforeAll(async () => { + const pruning = pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); + await expect(pruning).resolves.toBeTruthy(); + + ledger = new IndyTestLedger({ + containerImageName, + containerImageVersion, + useRunningLedger, + emitContainerLogs: false, + logLevel: testLogLevel, + }); + await ledger.start(); + + addressInfo = (await Servers.listen({ + hostname: "127.0.0.1", + port: 0, + server, + })) as AddressInfo; + ({ address, port } = addressInfo); + apiHost = `http://${address}:${port}`; + apiConfig = new Configuration({ basePath: apiHost }); + apiClient = new AriesApiClient(apiConfig); + + connector = new PluginLedgerConnectorAries({ + instanceId: uuidV4(), + logLevel: testLogLevel, + walletPath: testWalletPath, + ariesAgents: [ + { + name: aliceAgentName, + walletKey: aliceAgentName, + indyNetworks: [await ledger.getIndyVdrPoolConfig(indyNamespace)], + inboundUrl: aliceInboundUrl, + autoAcceptConnections: true, + }, + { + name: bobAgentName, + walletKey: bobAgentName, + indyNetworks: [await ledger.getIndyVdrPoolConfig(indyNamespace)], + inboundUrl: bobInboundUrl, + autoAcceptConnections: true, + }, + ], + }); + + await connector.getOrCreateWebServices(); + await connector.registerWebServices(expressApp, wsApi); + await connector.onPluginInit(); + + // Import endorser DID for Alice Agent + await connector.importExistingIndyDidFromPrivateKey( + aliceAgentName, + ledger.getEndorserDidSeed(), + indyNamespace, + ); + }); + + afterAll(async () => { + log.info("FINISHING THE TESTS"); + + if (server) { + log.info("Stop the connector server..."); + await Servers.shutdown(server); + } + + if (connector) { + log.info("Stop the connector..."); + await connector.shutdown(); + } + + if (ledger && !leaveLedgerRunning) { + log.info("Stop the indy ledger..."); + await ledger.stop(); + await ledger.destroy(); + } + + log.info("Prune Docker..."); + await pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); + + try { + await rm(testWalletPath, { + recursive: true, + force: true, + maxRetries: 5, + }); + log.info(`${testWalletPath} remove successfully.`); + } catch (error) { + log.warn(`${testWalletPath} could not be removed:`, error); + } + }); + + test("Aries agent created on plugin init", async () => { + const allAgents = await connector.getAgents(); + expect(allAgents.length).toBe(2); + }); + + test("Invitation with custom domain works", async () => { + const customDomain = "https://my-custom-domain.org"; + const invitationResponse = await apiClient.createNewConnectionInvitationV1({ + agentName: aliceAgentName, + invitationDomain: customDomain, + }); + const { invitationUrl, outOfBandId } = invitationResponse.data; + expect(invitationUrl).toBeTruthy(); + expect(outOfBandId).toBeTruthy(); + expect(new URL(invitationUrl).origin).toEqual(customDomain); + }); + + test("Connect to another aries agent using invitation URL", async () => { + // Connect Alice and Bob + log.info("1. Create invitation from Alice"); + const invitationResponse = await apiClient.createNewConnectionInvitationV1({ + agentName: aliceAgentName, + }); + const invitationUrl = invitationResponse.data.invitationUrl; + const aliceOutOfBandId = invitationResponse.data.outOfBandId; + expect(invitationUrl).toBeTruthy(); + expect(aliceOutOfBandId).toBeTruthy(); + const isPeerConnectedPromise = apiClient.waitForInvitedPeerConnectionV1( + aliceAgentName, + aliceOutOfBandId, + ); + + log.info("2. Accept invitation as Bob"); + const acceptResponse = await apiClient.acceptInvitationV1({ + agentName: bobAgentName, + invitationUrl: invitationUrl, + }); + const bobOutOfBandId = acceptResponse.data.outOfBandId; + expect(bobOutOfBandId).toBeTruthy(); + + log.info("3. Wait for connection readiness on Bob side"); + await apiClient.waitForConnectionReadyV1(bobAgentName, bobOutOfBandId); + + log.info("4. Wait for connection readiness on Alice side"); + await isPeerConnectedPromise; + + log.info("Connection between Alice and Bob connectors established!"); + + // Check getConnectionsV1 endpoint + const bobConnectionsResponse = await apiClient.getConnectionsV1({ + agentName: bobAgentName, + filter: { + state: "completed", + }, + }); + expect(bobConnectionsResponse).toBeTruthy(); + expect(bobConnectionsResponse.data).toBeTruthy(); + const firstBobConnection = + bobConnectionsResponse.data.pop() as AgentConnectionRecordV1; + expect(firstBobConnection).toBeTruthy(); + expect(firstBobConnection.state).toEqual("completed"); + expect(firstBobConnection.isReady).toBeTrue(); + }); +}); diff --git a/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/unit/api-surface.test.ts b/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/unit/api-surface.test.ts new file mode 100644 index 0000000000..34aba3a0ae --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/src/test/typescript/unit/api-surface.test.ts @@ -0,0 +1,6 @@ +import * as apiSurface from "../../../main/typescript/public-api"; +import "jest-extended"; + +test("Library can be loaded", async () => { + expect(apiSurface).toBeTruthy(); +}); diff --git a/packages/cactus-plugin-ledger-connector-aries/tsconfig.json b/packages/cactus-plugin-ledger-connector-aries/tsconfig.json new file mode 100644 index 0000000000..67a17b3ca7 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-aries/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./dist/lib", + "declarationDir": "./dist/lib", + "resolveJsonModule": true, + "rootDir": "./src", + "tsBuildInfoFile": "../../.build-cache/cactus-plugin-ledger-connector-aries.tsbuildinfo" + }, + "include": ["./src", "src/**/*.json"], + "references": [ + { + "path": "../cactus-common/tsconfig.json" + }, + { + "path": "../cactus-core/tsconfig.json" + }, + { + "path": "../cactus-core-api/tsconfig.json" + }, + { + "path": "../cactus-test-tooling/tsconfig.json" + } + ] +} diff --git a/packages/cactus-plugin-ledger-connector-ethereum/openapitools.json b/packages/cactus-plugin-ledger-connector-ethereum/openapitools.json index 03392961f6..225ff1aaae 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/openapitools.json +++ b/packages/cactus-plugin-ledger-connector-ethereum/openapitools.json @@ -2,6 +2,6 @@ "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", "spaces": 2, "generator-cli": { - "version": "6.3.0" + "version": "6.6.0" } } diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION index e7e42a4b58..cd802a1ec4 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION @@ -1 +1 @@ -6.3.0 \ No newline at end of file +6.6.0 \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/openapitools.json b/packages/cactus-plugin-ledger-connector-sawtooth/openapitools.json index 03392961f6..225ff1aaae 100644 --- a/packages/cactus-plugin-ledger-connector-sawtooth/openapitools.json +++ b/packages/cactus-plugin-ledger-connector-sawtooth/openapitools.json @@ -2,6 +2,6 @@ "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", "spaces": 2, "generator-cli": { - "version": "6.3.0" + "version": "6.6.0" } } diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION index e7e42a4b58..cd802a1ec4 100644 --- a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION @@ -1 +1 @@ -6.3.0 \ No newline at end of file +6.6.0 \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/VERSION b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/VERSION index e7e42a4b58..cd802a1ec4 100644 --- a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/VERSION +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/.openapi-generator/VERSION @@ -1 +1 @@ -6.3.0 \ No newline at end of file +6.6.0 \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/api.ts b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/api.ts index 16c82bb15b..a6f1444c4f 100644 --- a/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/api.ts +++ b/packages/cactus-plugin-ledger-connector-sawtooth/src/main/typescript/sawtooth-api/api.ts @@ -99,19 +99,6 @@ export interface BatchStatusesGet200Response { */ 'link'?: string; } -/** - * - * @export - * @interface BatchStatusesGet200Response1 - */ -export interface BatchStatusesGet200Response1 { - /** - * - * @type {Array} - * @memberof BatchStatusesGet200Response1 - */ - 'data'?: Array; -} /** * * @export @@ -172,6 +159,19 @@ export interface BatchStatusesInnerInvalidTransactionsInner { */ 'extended_data'?: string; } +/** + * + * @export + * @interface BatchStatusesPost200Response + */ +export interface BatchStatusesPost200Response { + /** + * + * @type {Array} + * @memberof BatchStatusesPost200Response + */ + 'data'?: Array; +} /** * * @export @@ -231,13 +231,13 @@ export interface BatchesGet200Response { /** * * @export - * @interface BatchesGet202Response + * @interface BatchesPost202Response */ -export interface BatchesGet202Response { +export interface BatchesPost202Response { /** * * @type {string} - * @memberof BatchesGet202Response + * @memberof BatchesPost202Response */ 'link'?: string; } @@ -1442,7 +1442,7 @@ export const DefaultApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async batchStatusesPost(requestBody: Array, wait?: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + async batchStatusesPost(requestBody: Array, wait?: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.batchStatusesPost(requestBody, wait, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, @@ -1478,7 +1478,7 @@ export const DefaultApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async batchesPost(batchList: BatchList, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + async batchesPost(batchList: BatchList, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.batchesPost(batchList, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, @@ -1631,7 +1631,7 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa * @param {*} [options] Override http request option. * @throws {RequiredError} */ - batchStatusesPost(requestBody: Array, wait?: number, options?: any): AxiosPromise { + batchStatusesPost(requestBody: Array, wait?: number, options?: any): AxiosPromise { return localVarFp.batchStatusesPost(requestBody, wait, options).then((request) => request(axios, basePath)); }, /** @@ -1664,7 +1664,7 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa * @param {*} [options] Override http request option. * @throws {RequiredError} */ - batchesPost(batchList: BatchList, options?: any): AxiosPromise { + batchesPost(batchList: BatchList, options?: any): AxiosPromise { return localVarFp.batchesPost(batchList, options).then((request) => request(axios, basePath)); }, /** diff --git a/tsconfig.json b/tsconfig.json index 1596e77339..f33c9d1570 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -49,6 +49,9 @@ { "path": "./packages/cactus-plugin-keychain-vault/tsconfig.json" }, + { + "path": "./packages/cactus-plugin-ledger-connector-aries/tsconfig.json" + }, { "path": "./packages/cactus-plugin-ledger-connector-besu/tsconfig.json" }, diff --git a/yarn.lock b/yarn.lock index 63f7c46c17..cc9ef31c08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -606,23 +606,23 @@ __metadata: languageName: node linkType: hard -"@aries-framework/anoncreds-rs@npm:0.5.0-alpha.58": - version: 0.5.0-alpha.58 - resolution: "@aries-framework/anoncreds-rs@npm:0.5.0-alpha.58" +"@aries-framework/anoncreds-rs@npm:0.5.0-alpha.71": + version: 0.5.0-alpha.71 + resolution: "@aries-framework/anoncreds-rs@npm:0.5.0-alpha.71" dependencies: - "@aries-framework/anoncreds": 0.5.0-alpha.58+17854790 - "@aries-framework/core": 0.5.0-alpha.58+17854790 + "@aries-framework/anoncreds": 0.5.0-alpha.71+4c08179b + "@aries-framework/core": 0.5.0-alpha.71+4c08179b class-transformer: ^0.5.1 class-validator: 0.14.0 rxjs: ^7.2.0 tsyringe: ^4.8.0 peerDependencies: "@hyperledger/anoncreds-shared": ^0.2.0-dev.4 - checksum: 8df978697e94920c1c65cb8bb6ecd5634d0e88ce175f5634f629d5aecffb4e7f6d30235cf848e59053d367c07737f2ad4ef3e1663738c6518aa8b45d29d2c627 + checksum: c8a6e67a4f73c0542f16967a62a24ca02e8d56d6d2a81ebab7af71ddad536d8bd6d641bd774fb71af90625164f19f2a5a4f8784c14b5a288e31880a51b9f6b0c languageName: node linkType: hard -"@aries-framework/anoncreds@npm:0.5.0-alpha.58, @aries-framework/anoncreds@npm:0.5.0-alpha.58+17854790": +"@aries-framework/anoncreds@npm:0.5.0-alpha.58+17854790": version: 0.5.0-alpha.58 resolution: "@aries-framework/anoncreds@npm:0.5.0-alpha.58" dependencies: @@ -635,6 +635,19 @@ __metadata: languageName: node linkType: hard +"@aries-framework/anoncreds@npm:0.5.0-alpha.71, @aries-framework/anoncreds@npm:0.5.0-alpha.71+4c08179b": + version: 0.5.0-alpha.71 + resolution: "@aries-framework/anoncreds@npm:0.5.0-alpha.71" + dependencies: + "@aries-framework/core": 0.5.0-alpha.71+4c08179b + bn.js: ^5.2.1 + class-transformer: 0.5.1 + class-validator: 0.14.0 + reflect-metadata: ^0.1.13 + checksum: b64294cd86d24c2f51dc99709e9285eeedec0554ff4c682489964634885259c136a725630a1dfe9177663b1d4fe28d301f2b3d595add528c8978088e8a685c32 + languageName: node + linkType: hard + "@aries-framework/askar@npm:0.5.0-alpha.58": version: 0.5.0-alpha.58 resolution: "@aries-framework/askar@npm:0.5.0-alpha.58" @@ -651,6 +664,22 @@ __metadata: languageName: node linkType: hard +"@aries-framework/askar@npm:0.5.0-alpha.71": + version: 0.5.0-alpha.71 + resolution: "@aries-framework/askar@npm:0.5.0-alpha.71" + dependencies: + "@aries-framework/core": 0.5.0-alpha.71+4c08179b + bn.js: ^5.2.1 + class-transformer: 0.5.1 + class-validator: 0.14.0 + rxjs: ^7.2.0 + tsyringe: ^4.8.0 + peerDependencies: + "@hyperledger/aries-askar-shared": ^0.2.0-dev.1 + checksum: 7c1ef48fa7a03dc21c5ddfc66dbefe4bb71701c715384b6433cbaa6864f9b1a22b512cd98d8ced945d95698e836e3bbe87bc534cd4a0b3d2ed79d722a33e117d + languageName: node + linkType: hard + "@aries-framework/core@npm:0.5.0-alpha.58, @aries-framework/core@npm:0.5.0-alpha.58+17854790": version: 0.5.0-alpha.58 resolution: "@aries-framework/core@npm:0.5.0-alpha.58" @@ -686,19 +715,53 @@ __metadata: languageName: node linkType: hard -"@aries-framework/indy-sdk@npm:0.5.0-alpha.58": - version: 0.5.0-alpha.58 - resolution: "@aries-framework/indy-sdk@npm:0.5.0-alpha.58" +"@aries-framework/core@npm:0.5.0-alpha.71, @aries-framework/core@npm:0.5.0-alpha.71+4c08179b": + version: 0.5.0-alpha.71 + resolution: "@aries-framework/core@npm:0.5.0-alpha.71" dependencies: - "@aries-framework/anoncreds": 0.5.0-alpha.58+17854790 - "@aries-framework/core": 0.5.0-alpha.58+17854790 + "@digitalcredentials/jsonld": ^5.2.1 + "@digitalcredentials/jsonld-signatures": ^9.3.1 + "@digitalcredentials/vc": ^1.1.2 + "@multiformats/base-x": ^4.0.1 + "@stablelib/ed25519": ^1.0.2 + "@stablelib/random": ^1.0.1 + "@stablelib/sha256": ^1.0.1 + "@types/ws": ^8.5.4 + abort-controller: ^3.0.0 + big-integer: ^1.6.51 + borc: ^3.0.0 + buffer: ^6.0.3 + class-transformer: 0.5.1 + class-validator: 0.14.0 + did-resolver: ^4.1.0 + lru_map: ^0.4.1 + luxon: ^3.3.0 + make-error: ^1.3.6 + object-inspect: ^1.10.3 + query-string: ^7.0.1 + reflect-metadata: ^0.1.13 + rxjs: ^7.2.0 + tsyringe: ^4.8.0 + uuid: ^9.0.0 + varint: ^6.0.0 + web-did-resolver: ^2.0.21 + checksum: 04ed30f661c342e99cf91ed7f487afb73e832aca2f99a702821188b7f8641d013868900464eb3cccc25f57eb4a8f4250eccaf6640641f8e84a169e70d69d867a + languageName: node + linkType: hard + +"@aries-framework/indy-sdk@npm:0.5.0-alpha.71": + version: 0.5.0-alpha.71 + resolution: "@aries-framework/indy-sdk@npm:0.5.0-alpha.71" + dependencies: + "@aries-framework/anoncreds": 0.5.0-alpha.71+4c08179b + "@aries-framework/core": 0.5.0-alpha.71+4c08179b "@stablelib/ed25519": ^1.0.3 "@types/indy-sdk": 1.16.27 class-transformer: 0.5.1 class-validator: 0.14.0 rxjs: ^7.2.0 tsyringe: ^4.8.0 - checksum: 6823f2cdb5401736d64f72dee80e5ec543547b0778d83c0392304f63b093ec049c17fe6d1ce9792ae8912e4888a8a85e3449ad2f0963713ae3199f395770ea9e + checksum: 2c90ac287c4fc97845fea664bf9afe6e4e66ca141284c5e327a27327fbec1abe0fa2556a5dbc4ab81e437ad78c47a558a3fa33b430a6f2684d1ed733f409544c languageName: node linkType: hard @@ -714,6 +777,18 @@ __metadata: languageName: node linkType: hard +"@aries-framework/indy-vdr@npm:0.5.0-alpha.71": + version: 0.5.0-alpha.71 + resolution: "@aries-framework/indy-vdr@npm:0.5.0-alpha.71" + dependencies: + "@aries-framework/anoncreds": 0.5.0-alpha.71+4c08179b + "@aries-framework/core": 0.5.0-alpha.71+4c08179b + peerDependencies: + "@hyperledger/indy-vdr-shared": ^0.2.0-dev.5 + checksum: 7e565c5633edd176433d30c6ce3092d4f8a528793e83cb185220c8c8e35c9b987de1bfba41a5cb3190e4c7a8ac833fe03852b53064a1ecdba53a428d103f2e69 + languageName: node + linkType: hard + "@aries-framework/node@npm:0.5.0-alpha.58": version: 0.5.0-alpha.58 resolution: "@aries-framework/node@npm:0.5.0-alpha.58" @@ -729,6 +804,20 @@ __metadata: languageName: node linkType: hard +"@aries-framework/node@npm:0.5.0-alpha.71": + version: 0.5.0-alpha.71 + resolution: "@aries-framework/node@npm:0.5.0-alpha.71" + dependencies: + "@2060.io/ffi-napi": ^4.0.8 + "@2060.io/ref-napi": ^3.0.6 + "@aries-framework/core": 0.5.0-alpha.71+4c08179b + "@types/express": ^4.17.15 + express: ^4.17.1 + ws: ^8.13.0 + checksum: 6f6c71eb2a6b6a917b1d86ec5346a73b9a5d249566a5eb1727049c97c8ba6c53ff64df14c6e053aa8291ebc194ed6c10db0de349e969aea353c4b83ff01460c3 + languageName: node + linkType: hard + "@assemblyscript/loader@npm:^0.10.1": version: 0.10.1 resolution: "@assemblyscript/loader@npm:0.10.1" @@ -7285,22 +7374,22 @@ __metadata: languageName: unknown linkType: soft -"@hyperledger/cactus-example-discounted-asset-trade-client@2.0.0-alpha.2, @hyperledger/cactus-example-discounted-asset-trade-client@workspace:examples/cactus-example-discounted-asset-trade-client": +"@hyperledger/cactus-example-discounted-asset-trade-client@workspace:examples/cactus-example-discounted-asset-trade-client": version: 0.0.0-use.local resolution: "@hyperledger/cactus-example-discounted-asset-trade-client@workspace:examples/cactus-example-discounted-asset-trade-client" dependencies: - "@aries-framework/anoncreds": 0.5.0-alpha.58 - "@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 + "@aries-framework/anoncreds": 0.5.0-alpha.71 + "@aries-framework/anoncreds-rs": 0.5.0-alpha.71 + "@aries-framework/askar": 0.5.0-alpha.71 + "@aries-framework/core": 0.5.0-alpha.71 + "@aries-framework/indy-sdk": 0.5.0-alpha.71 + "@aries-framework/indy-vdr": 0.5.0-alpha.71 + "@aries-framework/node": 0.5.0-alpha.71 "@hyperledger/anoncreds-nodejs": 0.2.0-dev.4 "@hyperledger/aries-askar-nodejs": 0.2.0-dev.1 "@hyperledger/indy-vdr-nodejs": 0.2.0-dev.3 "@types/inquirer": 8.2.6 - axios: 1.5.1 + axios: 1.6.0 inquirer: 8.2.6 loglevel: 1.8.1 bin: @@ -7317,8 +7406,8 @@ __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-example-discounted-asset-trade-client": 2.0.0-alpha.2 "@hyperledger/cactus-plugin-keychain-memory": 2.0.0-alpha.2 + "@hyperledger/cactus-plugin-ledger-connector-aries": 2.0.0-alpha.2 "@hyperledger/cactus-plugin-ledger-connector-ethereum": 2.0.0-alpha.2 "@hyperledger/cactus-plugin-ledger-connector-fabric": 2.0.0-alpha.2 "@types/elliptic": 6.4.14 @@ -7776,6 +7865,38 @@ __metadata: languageName: unknown linkType: soft +"@hyperledger/cactus-plugin-ledger-connector-aries@2.0.0-alpha.2, @hyperledger/cactus-plugin-ledger-connector-aries@workspace:packages/cactus-plugin-ledger-connector-aries": + version: 0.0.0-use.local + resolution: "@hyperledger/cactus-plugin-ledger-connector-aries@workspace:packages/cactus-plugin-ledger-connector-aries" + dependencies: + "@aries-framework/anoncreds": 0.5.0-alpha.71 + "@aries-framework/anoncreds-rs": 0.5.0-alpha.71 + "@aries-framework/askar": 0.5.0-alpha.71 + "@aries-framework/core": 0.5.0-alpha.71 + "@aries-framework/indy-vdr": 0.5.0-alpha.71 + "@aries-framework/node": 0.5.0-alpha.71 + "@hyperledger/anoncreds-nodejs": 0.2.0-dev.4 + "@hyperledger/aries-askar-nodejs": 0.2.0-dev.1 + "@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-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/uuid": 9.0.6 + axios: 1.6.0 + body-parser: 1.20.2 + express: 4.18.2 + jest: 29.6.2 + jest-extended: 4.0.1 + rxjs: 7.8.1 + socket.io: 4.5.4 + socket.io-client-fixed-types: 4.5.4 + uuid: 9.0.1 + languageName: unknown + linkType: soft + "@hyperledger/cactus-plugin-ledger-connector-besu@2.0.0-alpha.2, @hyperledger/cactus-plugin-ledger-connector-besu@workspace:packages/cactus-plugin-ledger-connector-besu": version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-ledger-connector-besu@workspace:packages/cactus-plugin-ledger-connector-besu"